oraclecloud: add aliases (#2627)

This commit is contained in:
Ludovic Fernandez 2025-08-24 22:18:26 +02:00 committed by GitHub
commit 784ce2be95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 137 additions and 51 deletions

View file

@ -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`)

View file

@ -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" %}}).

View file

@ -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
}

View file

@ -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)

View file

@ -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)"

View file

@ -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,
}
}