Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/cmd_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func run(ctx context.Context, cmd *cli.Command) error {
return fmt.Errorf("set up account: %w", err)
}

_, err = dotenv.Load(dotenv.BaseFilePrefix)
_, err = dotenv.Load(cmd.String(flags.FlgEnvFile))
if err != nil {
return fmt.Errorf("set up environment: %w", err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/internal/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type DNSChallenge struct {
Propagation *Propagation `yaml:"propagation,omitempty"`
DNSTimeout int `yaml:"dnsTimeout,omitempty"`
Resolvers []string `yaml:"resolvers,omitempty"`
EnvFile string `yaml:"envFile,omitempty"`
}

type DNSPersistChallenge struct {
Expand Down
5 changes: 3 additions & 2 deletions cmd/internal/configuration/testdata/reference.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This is not a valid configuration file.
# This must be moved/used as a reference inside the documentation.
# Don't use it.

storage: /tmp/lego
storage: /tmp/lego/
networkStack: ipv6only
userAgent: foo

Expand Down Expand Up @@ -40,6 +40,7 @@ challenges:
three:
dns:
provider: cloudflare
envFile: /tmp/secrets/.env.cf
propagation:
disableAuthoritativeNameservers: true
disableRecursiveNameservers: true
Expand Down
13 changes: 12 additions & 1 deletion cmd/internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ func createACMEClientFlags() []cli.Flag {
}

func createChallengesFlags() []cli.Flag {
var flags []cli.Flag
flags := []cli.Flag{
CreateEnvFileFlag(),
}

flags = append(flags, createHTTPChallengeFlags()...)
flags = append(flags, createTLSChallengeFlags()...)
Expand Down Expand Up @@ -765,6 +767,15 @@ func CreatePathFlag(forceCreation bool) cli.Flag {
}
}

func CreateEnvFileFlag() cli.Flag {
return &cli.StringFlag{
Category: categoryStorage,
Name: FlgEnvFile,
Sources: cli.EnvVars(toEnvName(FlgEnvFile)),
Usage: "The path to the dotenv file.",
}
}

func createLogFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Expand Down
1 change: 1 addition & 0 deletions cmd/internal/flags/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const (
// Flag names related to the storage.
const (
FlgPath = "path"
FlgEnvFile = "env-file"
FlgPEM = "pem"
FlgPFX = "pfx"
FlgPFXPass = "pfx.password"
Expand Down
2 changes: 1 addition & 1 deletion cmd/internal/root/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func process(ctx context.Context, cfg *configuration.Configuration) error {

func processChallenges(ctx context.Context, lazyClient lzSetUp, chlgNode *configuration.ChallengeNode, store *storage.Storage, hookManager *hook.Manager, networkStack challenge.NetworkStack) error {
if chlgNode.DNS != nil {
cleanUp, err := dotenv.Load(dotenv.BaseFilePrefix, dotenv.BaseFilePrefix+"."+chlgNode.ID)
cleanUp, err := dotenv.Load(chlgNode.DNS.EnvFile)

defer cleanUp()

Expand Down
29 changes: 24 additions & 5 deletions docs/content/obtain/dns01.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,38 @@ CLOUDFLARE_API_KEY='yourprivatecloudflareapikey' \

You can also use a dotenv file.

When using `lego run`, the file `.env` is automatically loaded.
When using `lego run`, you can pass the path to the dotenv file with the `--env-file` flag.

When using `lego`, the environment variables are loaded from the `.env` file, and from the file `.env.<challenge_name>` where `<challenge_name>` is the name defined in the configuration file as the challenge name (not the provider name).
When using `lego`, the environment variables are loaded from the file defined by `envFile` in the configuration file for the DNS provider.

The environment variables defined in the file `.env.<challenge_name>` overrides the environment variables defined in file `.env` ("merge").
{{< tabs >}}
{{% tab title=".lego.yml" %}}

For example, in the previous example with the file configuration, you can create a file `.env.cf` to define the credentials for the Cloudflare provider insead of defining them in the environment variables.
```yaml
challenges:
cf:
dns:
provider: cloudflare
envFile: .env.cf

```ini
certificates:
foo:
domains:
- example.com
- '*.example.com'
```

{{% /tab %}}
{{% tab title=".env.cf" %}}

```dotenv
CLOUDFLARE_EMAIL=you@example.com
CLOUDFLARE_API_KEY=yourprivatecloudflareapikey
```

{{% /tab %}}
{{< /tabs >}}

## Tips

{{% notice title="For a zone that has multiple SOAs" icon="info-circle" %}}
Expand Down
7 changes: 6 additions & 1 deletion docs/content/references/ref-file/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The configuration file can be validated with the JSON Schema: [lego.jsonschema.j
# Path to the directory to use for storing the data.
#
# Default: ./lego
storage: /tmp/lego
storage: /tmp/lego/

# The network stack to use.
# It can be:
Expand Down Expand Up @@ -259,6 +259,11 @@ challenges:
# Required.
provider: cloudflare

# The path to the dotenv file containing the credentials.
#
# Optional.
envFile: /tmp/secrets/.env

# The configuration related to propagation check.
#
# Optional.
Expand Down
1 change: 1 addition & 0 deletions docs/data/zz_cli_help.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docs/static/lego.jsonschema.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@
},
"propagation": {
"$ref": "#/definitions/propagationSettings"
},
"envFile": {
"type": "string",
"default": ""
}
}
},
Expand Down
1 change: 0 additions & 1 deletion e2e/configuration/challenges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ var load = loader.EnvLoader{
},
LegoOptions: []string{
"LEGO_CA_CERTIFICATES=../fixtures/certs/pebble.minica.pem",
"12b79c45_2153_4e99_9518_67b3350d878b=./fixtures",
"LEGO_DEBUG_ACME_HTTP_CLIENT=1",
},
ChallSrv: &loader.CmdOption{
Expand Down
1 change: 1 addition & 0 deletions e2e/configuration/fixtures/lego_dns-explicit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ challenges:
wait: 500ms
resolvers:
- :8853
envFile: ./fixtures/.env.lego.mychallenge

certificates:
'dns.localhost':
Expand Down
1 change: 1 addition & 0 deletions e2e/configuration/fixtures/lego_dns-simple.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ challenges:
wait: 500ms
resolvers:
- :8853
envFile: ./fixtures/.env.lego.mychallenge

certificates:
'dns.localhost':
Expand Down
1 change: 0 additions & 1 deletion e2e/dnschallenge/challenges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ var load = loader.EnvLoader{
},
LegoOptions: []string{
"LEGO_CA_CERTIFICATES=../fixtures/certs/pebble.minica.pem",
"12b79c45_2153_4e99_9518_67b3350d878b=./fixtures",
"LEGO_DEBUG_ACME_HTTP_CLIENT=1",
},
ChallSrv: &loader.CmdOption{
Expand Down
1 change: 1 addition & 0 deletions e2e/dnschallenge/dns_challenge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestChallengeDNS_Run(t *testing.T) {
"--dns", "exec",
"--dns.resolvers", ":8553",
"--dns.propagation.wait", "0",
"--env-file", "./fixtures/.env",
"-s", caDirectory,
"-d", testDomain2,
"-d", testDomain1,
Expand Down
22 changes: 8 additions & 14 deletions internal/dotenv/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,15 @@ import (
"log/slog"
"maps"
"os"
"path/filepath"
"strings"

"github.com/go-acme/lego/v5/log"
"github.com/joho/godotenv"
)

const BaseFilePrefix = ".env"

func Load(filenames ...string) (func(), error) {
// ONLY FOR TESTING PURPOSE: DON'T USE IT!!
prefix, ok := os.LookupEnv("12b79c45_2153_4e99_9518_67b3350d878b")
if ok {
var prefixed []string

for _, filename := range filenames {
prefixed = append(prefixed, filepath.Join(prefix, filename))
}

filenames = prefixed
if len(filenames) == 0 {
return noopCleanUp, nil
}

envs, err := read(filenames)
Expand Down Expand Up @@ -65,10 +55,14 @@ func read(filenames []string) (map[string]string, error) {
envMap := make(map[string]string)

for _, filename := range filenames {
if strings.TrimSpace(filename) == "" {
continue
}

_, err := os.Stat(filename)
if err != nil {
if os.IsNotExist(err) {
log.Debug("Environment file not found", slog.String("filename", filename))
log.Info("Environment file not found", slog.String("filename", filename))

continue
}
Expand Down
6 changes: 3 additions & 3 deletions internal/dotenv/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ func TestLoad(t *testing.T) {
},
{
desc: "non-existing file",
filenames: []string{filepath.Join("testdata", BaseFilePrefix+".non-existing")},
filenames: []string{filepath.Join("testdata", ".env.lego.non-existing")},
},
{
desc: "simple",
filenames: []string{filepath.Join("testdata", BaseFilePrefix)},
filenames: []string{filepath.Join("testdata", ".env.lego.bar")},
expected: []string{"LEGO_TEST_ENV_A=aGlobal", "LEGO_TEST_ENV_B=bGlobal"},
},
{
desc: "multiple files",
filenames: []string{filepath.Join("testdata", BaseFilePrefix), filepath.Join("testdata", BaseFilePrefix+".foo")},
filenames: []string{filepath.Join("testdata", ".env.lego.bar"), filepath.Join("testdata", ".env.lego.foo")},
expected: []string{"LEGO_TEST_ENV_A=aLocal", "LEGO_TEST_ENV_B=bGlobal"},
},
}
Expand Down
File renamed without changes.