lightninglabs / lightninglabs/loop

Add Secret type for reading database password from file

Open
#1,088 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

freebie
Dominant language
Go
Stars
595
Forks
135
Avg merge
1d 8h
Merged PRs (30d)
12

Description

Background

This issue tracks a follow-up suggestion from PR #1078: staticaddr: various fixes

Original Comment: https://github.com/lightninglabs/loop/pull/1078#discussion_r2894023390
Author: @starius

Nit for a follow-up PR if you like it.

I wish flags' parsing libraries provided a convenient way to pass password as files. Like:

Password  string `long:"password" description:"Database user's password." from:"file"`

Unfortunately it is not supported, but we can make a custom field type:

  type Secret string

  func (s *Secret) UnmarshalFlag(v string) error {
        // Example convention: @/path/to/file => read from file, else raw value
        if strings.HasPrefix(v, "@") {
                b, err := os.ReadFile(strings.TrimPrefix(v, "@"))
                if err != nil {
                        return err
                }
                *s = Secret(strings.TrimRight(string(b), "\r\n"))
                return nil
        }
        *s = Secret(v)
        return nil
  }

  type Opts struct {
        Password   Secret `long:"password" description:"Database user's password or @file to read the password from it."`
  }

Context

PR #1078 added a //nolint:gosec comment to suppress a linter warning about the Password field in PostgresConfig. The comment suggests a better long-term solution: instead of just suppressing the lint warning, introduce a custom Secret type that supports reading passwords from files, avoiding plaintext passwords in CLI arguments or config files.

Proposed Change

Introduce a Secret string type that implements UnmarshalFlag with a convention where values prefixed with @ are treated as file paths. This allows users to pass --password @/path/to/secret to read the password from a file, while still supporting raw string values for backward compatibility.

This is a security improvement — passing secrets via files avoids leaking them through process listings (ps aux) and shell history.

Implementation Approach

  • Add a Secret type in an appropriate package (e.g., loopdb or a shared config package).
  • Implement UnmarshalFlag(string) error on *Secret so go-flags calls it automatically.
  • If value starts with @, read the file at the remaining path and trim trailing newlines.
  • Otherwise, use the raw value directly.
  • Change PostgresConfig.Password from string to Secret in loopdb/postgres.go.
  • Update DSN() and any other callers to cast Secret back to string where needed.
  • Update the field description to document the @file convention.
  • The //nolint:gosec annotation can likely be removed since the field type is no longer a raw string named Password.

Related

  • PR #1078

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading loopdb/postgres.go and tracing PostgresConfig.Password through DSN() and its other callers. Check how go-flags invokes UnmarshalFlag, then implement the Secret behavior described in the issue and update the password field description. Done means @file values read and trim trailing newlines, raw values remain compatible, and the obsolete lint suppression is addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
databases, security
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.