From 784ce2be95e9d655e0b3dd7eb509eef79bf090a0 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Sun, 24 Aug 2025 22:18:26 +0200 Subject: [PATCH] oraclecloud: add aliases (#2627) --- cmd/zz_gen_cmd_dnshelp.go | 17 ++-- docs/content/dns/zz_gen_oraclecloud.md | 23 +++-- .../dns/oraclecloud/configurationprovider.go | 99 ++++++++++++++----- providers/dns/oraclecloud/oraclecloud.go | 31 +++++- providers/dns/oraclecloud/oraclecloud.toml | 23 +++-- providers/dns/oraclecloud/oraclecloud_test.go | 7 +- 6 files changed, 143 insertions(+), 57 deletions(-) diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index d53274139..9a4e24807 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -2518,12 +2518,12 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln(`Credentials:`) ew.writeln(` - "OCI_COMPARTMENT_OCID": Compartment OCID`) - ew.writeln(` - "OCI_PRIVKEY_FILE": Private key file (ignored if OCI_AUTH_TYPE=instance_principal)`) - ew.writeln(` - "OCI_PRIVKEY_PASS": Private key password (ignored if OCI_AUTH_TYPE=instance_principal)`) - ew.writeln(` - "OCI_PUBKEY_FINGERPRINT": Public key fingerprint (ignored if OCI_AUTH_TYPE=instance_principal)`) - ew.writeln(` - "OCI_REGION": Region (can be empty if OCI_AUTH_TYPE=instance_principal)`) - ew.writeln(` - "OCI_TENANCY_OCID": Tenancy OCID (ignored if OCI_AUTH_TYPE=instance_principal)`) - ew.writeln(` - "OCI_USER_OCID": User OCID (ignored if OCI_AUTH_TYPE=instance_principal)`) + ew.writeln(` - "OCI_FINGERPRINT": Public key fingerprint (ignored if 'OCI_AUTH_TYPE=instance_principal')`) + ew.writeln(` - "OCI_PRIVATE_KEY_PASSWORD": Private key password (ignored if 'OCI_AUTH_TYPE=instance_principal')`) + ew.writeln(` - "OCI_PRIVATE_KEY_PATH": Private key file (ignored if 'OCI_AUTH_TYPE=instance_principal')`) + ew.writeln(` - "OCI_REGION": Region (it can be empty if 'OCI_AUTH_TYPE=instance_principal').`) + ew.writeln(` - "OCI_TENANCY_OCID": Tenancy OCID (ignored if 'OCI_AUTH_TYPE=instance_principal')`) + ew.writeln(` - "OCI_USER_OCID": User OCID (ignored if 'OCI_AUTH_TYPE=instance_principal')`) ew.writeln() ew.writeln(`Additional Configuration:`) @@ -2532,6 +2532,11 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln(` - "OCI_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`) ew.writeln(` - "OCI_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`) ew.writeln(` - "OCI_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`) + ew.writeln(` - "TF_VAR_fingerprint": Alias on 'OCI_FINGERPRINT'`) + ew.writeln(` - "TF_VAR_private_key_path": Alias on 'OCI_PRIVATE_KEY_PATH'`) + ew.writeln(` - "TF_VAR_region": Alias on 'OCI_REGION'`) + ew.writeln(` - "TF_VAR_tenancy_ocid": Alias on 'OCI_TENANCY_OCID'`) + ew.writeln(` - "TF_VAR_user_ocid": Alias on 'OCI_USER_OCID'`) ew.writeln() ew.writeln(`More information: https://go-acme.github.io/lego/dns/oraclecloud`) diff --git a/docs/content/dns/zz_gen_oraclecloud.md b/docs/content/dns/zz_gen_oraclecloud.md index 974466601..c43c24b21 100644 --- a/docs/content/dns/zz_gen_oraclecloud.md +++ b/docs/content/dns/zz_gen_oraclecloud.md @@ -27,11 +27,11 @@ Here is an example bash command using the Oracle Cloud provider: ```bash # Using API Key authentication: -OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \ -OCI_PRIVKEY_PASS="secret" \ +OCI_PRIVATE_KEY_PATH="~/.oci/oci_api_key.pem" \ +OCI_PRIVATE_KEY_PASSWORD="secret" \ OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \ OCI_USER_OCID="ocid1.user.oc1..secret" \ -OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ +OCI_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ OCI_REGION="us-phoenix-1" \ OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \ lego --email you@example.com --dns oraclecloud -d '*.example.com' -d example.com run @@ -51,12 +51,12 @@ lego --email you@example.com --dns oraclecloud -d '*.example.com' -d example.com | Environment Variable Name | Description | |-----------------------|-------------| | `OCI_COMPARTMENT_OCID` | Compartment OCID | -| `OCI_PRIVKEY_FILE` | Private key file (ignored if OCI_AUTH_TYPE=instance_principal) | -| `OCI_PRIVKEY_PASS` | Private key password (ignored if OCI_AUTH_TYPE=instance_principal) | -| `OCI_PUBKEY_FINGERPRINT` | Public key fingerprint (ignored if OCI_AUTH_TYPE=instance_principal) | -| `OCI_REGION` | Region (can be empty if OCI_AUTH_TYPE=instance_principal) | -| `OCI_TENANCY_OCID` | Tenancy OCID (ignored if OCI_AUTH_TYPE=instance_principal) | -| `OCI_USER_OCID` | User OCID (ignored if OCI_AUTH_TYPE=instance_principal) | +| `OCI_FINGERPRINT` | Public key fingerprint (ignored if `OCI_AUTH_TYPE=instance_principal`) | +| `OCI_PRIVATE_KEY_PASSWORD` | Private key password (ignored if `OCI_AUTH_TYPE=instance_principal`) | +| `OCI_PRIVATE_KEY_PATH` | Private key file (ignored if `OCI_AUTH_TYPE=instance_principal`) | +| `OCI_REGION` | Region (it can be empty if `OCI_AUTH_TYPE=instance_principal`). | +| `OCI_TENANCY_OCID` | Tenancy OCID (ignored if `OCI_AUTH_TYPE=instance_principal`) | +| `OCI_USER_OCID` | User OCID (ignored if `OCI_AUTH_TYPE=instance_principal`) | The environment variable names can be suffixed by `_FILE` to reference a file instead of a value. More information [here]({{% ref "dns#configuration-and-credentials" %}}). @@ -71,6 +71,11 @@ More information [here]({{% ref "dns#configuration-and-credentials" %}}). | `OCI_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) | | `OCI_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) | | `OCI_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) | +| `TF_VAR_fingerprint` | Alias on `OCI_FINGERPRINT` | +| `TF_VAR_private_key_path` | Alias on `OCI_PRIVATE_KEY_PATH` | +| `TF_VAR_region` | Alias on `OCI_REGION` | +| `TF_VAR_tenancy_ocid` | Alias on `OCI_TENANCY_OCID` | +| `TF_VAR_user_ocid` | Alias on `OCI_USER_OCID` | The environment variable names can be suffixed by `_FILE` to reference a file instead of a value. More information [here]({{% ref "dns#configuration-and-credentials" %}}). diff --git a/providers/dns/oraclecloud/configurationprovider.go b/providers/dns/oraclecloud/configurationprovider.go index b18af50e4..97710108c 100644 --- a/providers/dns/oraclecloud/configurationprovider.go +++ b/providers/dns/oraclecloud/configurationprovider.go @@ -6,30 +6,41 @@ import ( "errors" "fmt" "os" + "slices" + "strings" + "github.com/go-acme/lego/v4/log" "github.com/go-acme/lego/v4/platform/config/env" "github.com/nrdcg/oci-go-sdk/common/v1065" ) type environmentConfigurationProvider struct { - values map[string]string - privateKeyPassphrase string + values map[string]string } -func newEnvironmentConfigurationProvider(values map[string]string) *environmentConfigurationProvider { - return &environmentConfigurationProvider{ - values: values, - privateKeyPassphrase: env.GetOrFile(EnvPrivKeyPass), - } -} - -func (p *environmentConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) { - privateKey, err := getPrivateKey(envPrivKey) +func newEnvironmentConfigurationProvider() (*environmentConfigurationProvider, error) { + values, err := env.GetWithFallback( + []string{EnvRegion, altEnvTFVarRegion}, + []string{EnvUserOCID, altEnvTFVarUserOCID}, + []string{EnvTenancyOCID, altEnvTFVarTenancyOCID}, + []string{EnvPubKeyFingerprint, altEnvFingerprint, altEnvTFVarFingerprint}, + ) if err != nil { return nil, err } - return common.PrivateKeyFromBytesWithPassword(privateKey, []byte(p.privateKeyPassphrase)) + return &environmentConfigurationProvider{ + values: values, + }, nil +} + +func (p *environmentConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) { + privateKey, err := getPrivateKey() + if err != nil { + return nil, err + } + + return common.PrivateKeyFromBytesWithPassword(privateKey, []byte(p.privateKeyPassword())) } func (p *environmentConfigurationProvider) KeyID() (string, error) { @@ -51,7 +62,7 @@ func (p *environmentConfigurationProvider) KeyID() (string, error) { return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil } -func (p *environmentConfigurationProvider) TenancyOCID() (value string, err error) { +func (p *environmentConfigurationProvider) TenancyOCID() (string, error) { return p.values[EnvTenancyOCID], nil } @@ -72,26 +83,62 @@ func (p *environmentConfigurationProvider) AuthType() (common.AuthConfig, error) return common.AuthConfig{AuthType: common.UnknownAuthenticationType}, errors.New("unsupported, keep the interface") } -func getPrivateKey(envVar string) ([]byte, error) { - envVarValue := os.Getenv(envVar) +func (p *environmentConfigurationProvider) privateKeyPassword() string { + return env.GetOneWithFallback(EnvPrivKeyPass, "", env.ParseString, altEnvPrivateKeyPassword, altEnvTFVarPrivateKeyPassword) +} + +func getPrivateKey() ([]byte, error) { + base64EnvKeys := []string{envPrivKey, altEnvPrivateKey} + + envVarValue := getEnvWithStrictFallback(base64EnvKeys...) if envVarValue != "" { bytes, err := base64.StdEncoding.DecodeString(envVarValue) if err != nil { - return nil, fmt.Errorf("failed to read base64 value %s (defined by env var %s): %w", envVarValue, envVar, err) + return nil, fmt.Errorf("failed to read base64 value %s (defined by env vars %s): %w", envVarValue, + strings.Join(base64EnvKeys, " or "), err) } + return bytes, nil } - fileVar := envVar + "_FILE" - fileVarValue := os.Getenv(fileVar) - if fileVarValue == "" { - return nil, fmt.Errorf("no value provided for: %s or %s", envVar, fileVar) + fileEnvKeys := []string{EnvPrivKeyFile, altEnvPrivateKeyPath, altEnvTFVarPrivateKeyPath} + + fileVarValue := getEnvFileWithStrictFallback(fileEnvKeys...) + if len(fileVarValue) == 0 { + return nil, fmt.Errorf("no value provided for: %s", + strings.Join(slices.Concat(base64EnvKeys, fileEnvKeys), " or "), + ) } - fileContents, err := os.ReadFile(fileVarValue) - if err != nil { - return nil, fmt.Errorf("failed to read the file %s (defined by env var %s): %w", fileVarValue, fileVar, err) - } - - return fileContents, nil + return fileVarValue, nil +} + +func getEnvWithStrictFallback(keys ...string) string { + for _, key := range keys { + envVarValue := os.Getenv(key) + if envVarValue != "" { + return envVarValue + } + } + + return "" +} + +func getEnvFileWithStrictFallback(keys ...string) []byte { + for _, key := range keys { + fileVarValue := os.Getenv(key) + if fileVarValue == "" { + continue + } + + fileContents, err := os.ReadFile(fileVarValue) + if err != nil { + log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, key, err) + return nil + } + + return fileContents + } + + return nil } diff --git a/providers/dns/oraclecloud/oraclecloud.go b/providers/dns/oraclecloud/oraclecloud.go index a5e97baf8..47902568c 100644 --- a/providers/dns/oraclecloud/oraclecloud.go +++ b/providers/dns/oraclecloud/oraclecloud.go @@ -32,12 +32,29 @@ const ( EnvUserOCID = envNamespace + "USER_OCID" EnvPubKeyFingerprint = envNamespace + "PUBKEY_FINGERPRINT" + altEnvPrivateKey = envNamespace + "PRIVATE_KEY" // alias on OCI_PRIVKEY + altEnvPrivateKeyPath = altEnvPrivateKey + "_PATH" // alias on OCI_PRIVKEY_FILE + altEnvPrivateKeyPassword = altEnvPrivateKey + "_PASSWORD" // alias on OCI_PRIVKEY_PASS + altEnvFingerprint = envNamespace + "FINGERPRINT" // alias on OCI_PUBKEY_FINGERPRINT + EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" EnvPollingInterval = envNamespace + "POLLING_INTERVAL" EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" ) +// https://github.com/oracle/oci-go-sdk/blob/7f425f74c74fd0c6a5acb74466c85eb5346e0092/common/client.go#L350 +// https://github.com/oracle/oci-go-sdk/blob/7f425f74c74fd0c6a5acb74466c85eb5346e0092/common/configuration.go#L174-L175 +const ( + altEnvTFVarNamespace = "TF_VAR_" + altEnvTFVarRegion = altEnvTFVarNamespace + "region" // alias on OCI_REGION + altEnvTFVarFingerprint = altEnvTFVarNamespace + "fingerprint" // alias on OCI_PUBKEY_FINGERPRINT + altEnvTFVarUserOCID = altEnvTFVarNamespace + "user_ocid" // alias on OCI_USER_OCID + altEnvTFVarTenancyOCID = altEnvTFVarNamespace + "tenancy_ocid" // alias on OCI_TENANCY_OCID + altEnvTFVarPrivateKeyPath = altEnvTFVarNamespace + "private_key_path" // alias on OCI_PRIVKEY_FILE + altEnvTFVarPrivateKeyPassword = altEnvTFVarNamespace + "private_key_password" // alias on OCI_PRIVKEY_PASS +) + var _ challenge.ProviderTimeout = (*DNSProvider)(nil) // Config is used to configure the creation of the DNSProvider. @@ -82,7 +99,9 @@ func NewDNSProvider() (*DNSProvider, error) { config.CompartmentID = values[EnvCompartmentOCID] - configurationProvider, err := auth.InstancePrincipalConfigurationProviderForRegion(common.Region(env.GetOrFile(EnvRegion))) + region := env.GetOneWithFallback(EnvRegion, "", env.ParseString, altEnvTFVarRegion) + + configurationProvider, err := auth.InstancePrincipalConfigurationProviderForRegion(common.Region(region)) if err != nil { return nil, fmt.Errorf("oraclecloud: %w", err) } @@ -90,13 +109,19 @@ func NewDNSProvider() (*DNSProvider, error) { config.OCIConfigProvider = configurationProvider default: - values, err := env.Get(envPrivKey, EnvTenancyOCID, EnvUserOCID, EnvPubKeyFingerprint, EnvRegion, EnvCompartmentOCID) + values, err := env.Get(EnvCompartmentOCID) if err != nil { return nil, fmt.Errorf("oraclecloud: %w", err) } config.CompartmentID = values[EnvCompartmentOCID] - config.OCIConfigProvider = newEnvironmentConfigurationProvider(values) + + ecp, err := newEnvironmentConfigurationProvider() + if err != nil { + return nil, fmt.Errorf("oraclecloud: %w", err) + } + + config.OCIConfigProvider = ecp } return NewDNSProviderConfig(config) diff --git a/providers/dns/oraclecloud/oraclecloud.toml b/providers/dns/oraclecloud/oraclecloud.toml index b02333219..f13cb1e1e 100644 --- a/providers/dns/oraclecloud/oraclecloud.toml +++ b/providers/dns/oraclecloud/oraclecloud.toml @@ -6,11 +6,11 @@ Since = "v2.3.0" Example = ''' # Using API Key authentication: -OCI_PRIVKEY_FILE="~/.oci/oci_api_key.pem" \ -OCI_PRIVKEY_PASS="secret" \ +OCI_PRIVATE_KEY_PATH="~/.oci/oci_api_key.pem" \ +OCI_PRIVATE_KEY_PASSWORD="secret" \ OCI_TENANCY_OCID="ocid1.tenancy.oc1..secret" \ OCI_USER_OCID="ocid1.user.oc1..secret" \ -OCI_PUBKEY_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ +OCI_FINGERPRINT="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00" \ OCI_REGION="us-phoenix-1" \ OCI_COMPARTMENT_OCID="ocid1.tenancy.oc1..secret" \ lego --email you@example.com --dns oraclecloud -d '*.example.com' -d example.com run @@ -25,14 +25,19 @@ lego --email you@example.com --dns oraclecloud -d '*.example.com' -d example.com [Configuration] [Configuration.Credentials] OCI_COMPARTMENT_OCID = "Compartment OCID" - OCI_REGION = "Region (can be empty if OCI_AUTH_TYPE=instance_principal)" - OCI_PRIVKEY_FILE = "Private key file (ignored if OCI_AUTH_TYPE=instance_principal)" - OCI_PRIVKEY_PASS = "Private key password (ignored if OCI_AUTH_TYPE=instance_principal)" - OCI_TENANCY_OCID = "Tenancy OCID (ignored if OCI_AUTH_TYPE=instance_principal)" - OCI_USER_OCID = "User OCID (ignored if OCI_AUTH_TYPE=instance_principal)" - OCI_PUBKEY_FINGERPRINT = "Public key fingerprint (ignored if OCI_AUTH_TYPE=instance_principal)" + OCI_REGION = "Region (it can be empty if `OCI_AUTH_TYPE=instance_principal`)." + OCI_PRIVATE_KEY_PATH = "Private key file (ignored if `OCI_AUTH_TYPE=instance_principal`)" + OCI_PRIVATE_KEY_PASSWORD = "Private key password (ignored if `OCI_AUTH_TYPE=instance_principal`)" + OCI_TENANCY_OCID = "Tenancy OCID (ignored if `OCI_AUTH_TYPE=instance_principal`)" + OCI_USER_OCID = "User OCID (ignored if `OCI_AUTH_TYPE=instance_principal`)" + OCI_FINGERPRINT = "Public key fingerprint (ignored if `OCI_AUTH_TYPE=instance_principal`)" [Configuration.Additional] OCI_AUTH_TYPE = "Authorization type. Possible values: 'instance_principal', '' (Default: '')" + TF_VAR_region = "Alias on `OCI_REGION`" + TF_VAR_fingerprint = "Alias on `OCI_FINGERPRINT`" + TF_VAR_user_ocid = "Alias on `OCI_USER_OCID`" + TF_VAR_tenancy_ocid = "Alias on `OCI_TENANCY_OCID`" + TF_VAR_private_key_path = "Alias on `OCI_PRIVATE_KEY_PATH`" OCI_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)" OCI_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)" OCI_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)" diff --git a/providers/dns/oraclecloud/oraclecloud_test.go b/providers/dns/oraclecloud/oraclecloud_test.go index 473fcd7ed..058566504 100644 --- a/providers/dns/oraclecloud/oraclecloud_test.go +++ b/providers/dns/oraclecloud/oraclecloud_test.go @@ -72,7 +72,7 @@ func TestNewDNSProvider(t *testing.T) { { desc: "missing credentials", envVars: map[string]string{}, - expected: "oraclecloud: some credentials information are missing: OCI_PRIVKEY,OCI_TENANCY_OCID,OCI_USER_OCID,OCI_PUBKEY_FINGERPRINT,OCI_REGION,OCI_COMPARTMENT_OCID", + expected: "oraclecloud: some credentials information are missing: OCI_COMPARTMENT_OCID", }, { desc: "missing CompartmentID", @@ -98,7 +98,7 @@ func TestNewDNSProvider(t *testing.T) { EnvRegion: "us-phoenix-1", EnvCompartmentOCID: "123", }, - expected: "oraclecloud: some credentials information are missing: OCI_PRIVKEY", + expected: "oraclecloud: can not create client, bad configuration: no value provided for: OCI_PRIVKEY or OCI_PRIVATE_KEY or OCI_PRIVKEY_FILE or OCI_PRIVATE_KEY_PATH or TF_VAR_private_key_path", }, { desc: "missing OCI_PRIVKEY_PASS", @@ -362,13 +362,12 @@ func mockConfigurationProvider(keyPassphrase string) *environmentConfigurationPr return &environmentConfigurationProvider{ values: map[string]string{ EnvCompartmentOCID: "test", - EnvPrivKeyPass: "test", + EnvPrivKeyPass: keyPassphrase, EnvTenancyOCID: "test", EnvUserOCID: "test", EnvPubKeyFingerprint: "test", EnvRegion: "test", }, - privateKeyPassphrase: keyPassphrase, } }