diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index cc672d04..8c5a2161 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -142,6 +142,7 @@ func displayDNSHelp(name string) error { ew.writeln(`Credentials:`) ew.writeln(` - "ALICLOUD_ACCESS_KEY": Access key ID`) + ew.writeln(` - "ALICLOUD_RAM_ROLE": Your instance RAM role (https://www.alibabacloud.com/help/doc-detail/54579.htm)`) ew.writeln(` - "ALICLOUD_SECRET_KEY": Access Key secret`) ew.writeln(` - "ALICLOUD_SECURITY_TOKEN": STS Security Token (optional)`) ew.writeln() diff --git a/docs/content/dns/zz_gen_alidns.md b/docs/content/dns/zz_gen_alidns.md index daf8512c..17c7177a 100644 --- a/docs/content/dns/zz_gen_alidns.md +++ b/docs/content/dns/zz_gen_alidns.md @@ -21,8 +21,14 @@ Configuration for [Alibaba Cloud DNS](https://www.alibabacloud.com/product/dns). Here is an example bash command using the Alibaba Cloud DNS provider: ```bash +# Setup using instance RAM role +ALICLOUD_RAM_ROLE=lego \ +lego --email myemail@example.com --dns alidns --domains my.example.org run + +# Or, using credentials ALICLOUD_ACCESS_KEY=abcdefghijklmnopqrstuvwx \ -ALICLOUD_SECRET_KEY=xxxxxxx \ +ALICLOUD_SECRET_KEY=your-secret-key \ +ALICLOUD_SECURITY_TOKEN=your-sts-token \ lego --email myemail@example.com --dns alidns --domains my.example.org run ``` @@ -34,6 +40,7 @@ lego --email myemail@example.com --dns alidns --domains my.example.org run | Environment Variable Name | Description | |-----------------------|-------------| | `ALICLOUD_ACCESS_KEY` | Access key ID | +| `ALICLOUD_RAM_ROLE` | Your instance RAM role (https://www.alibabacloud.com/help/doc-detail/54579.htm) | | `ALICLOUD_SECRET_KEY` | Access Key secret | | `ALICLOUD_SECURITY_TOKEN` | STS Security Token (optional) | diff --git a/providers/dns/alidns/alidns.go b/providers/dns/alidns/alidns.go index 8754edf1..9a5d8169 100644 --- a/providers/dns/alidns/alidns.go +++ b/providers/dns/alidns/alidns.go @@ -23,6 +23,7 @@ const defaultRegionID = "cn-hangzhou" const ( envNamespace = "ALICLOUD_" + EnvRAMRole = envNamespace + "RAM_ROLE" EnvAccessKey = envNamespace + "ACCESS_KEY" EnvSecretKey = envNamespace + "SECRET_KEY" EnvSecurityToken = envNamespace + "SECURITY_TOKEN" @@ -36,6 +37,7 @@ const ( // Config is used to configure the creation of the DNSProvider. type Config struct { + RAMRole string APIKey string SecretKey string SecurityToken string @@ -63,18 +65,26 @@ type DNSProvider struct { } // NewDNSProvider returns a DNSProvider instance configured for Alibaba Cloud DNS. -// Credentials must be passed in the environment variables: +// - If you're using the instance RAM role, the RAM role environment variable must be passed in: ALICLOUD_RAM_ROLE. +// - Other than that, credentials must be passed in the environment variables: // ALICLOUD_ACCESS_KEY, ALICLOUD_SECRET_KEY, and optionally ALICLOUD_SECURITY_TOKEN. func NewDNSProvider() (*DNSProvider, error) { - values, err := env.Get(EnvAccessKey, EnvSecretKey) + config := NewDefaultConfig() + config.RegionID = env.GetOrFile(EnvRegionID) + + values, err := env.Get(EnvRAMRole) + if err == nil { + config.RAMRole = values[EnvRAMRole] + return NewDNSProviderConfig(config) + } + + values, err = env.Get(EnvAccessKey, EnvSecretKey) if err != nil { return nil, fmt.Errorf("alicloud: %w", err) } - config := NewDefaultConfig() config.APIKey = values[EnvAccessKey] config.SecretKey = values[EnvSecretKey] - config.RegionID = env.GetOrFile(EnvRegionID) config.SecurityToken = env.GetOrFile(EnvSecurityToken) return NewDNSProviderConfig(config) @@ -86,23 +96,24 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return nil, errors.New("alicloud: the configuration of the DNS provider is nil") } - if config.APIKey == "" || config.SecretKey == "" { - return nil, fmt.Errorf("alicloud: credentials missing") - } - if config.RegionID == "" { config.RegionID = defaultRegionID } - conf := sdk.NewConfig().WithTimeout(config.HTTPTimeout) - var credential auth.Credential - if config.SecurityToken == "" { - credential = credentials.NewAccessKeyCredential(config.APIKey, config.SecretKey) - } else { + switch { + case config.RAMRole != "": + credential = credentials.NewEcsRamRoleCredential(config.RAMRole) + case config.APIKey != "" && config.SecretKey != "" && config.SecurityToken != "": credential = credentials.NewStsTokenCredential(config.APIKey, config.SecretKey, config.SecurityToken) + case config.APIKey != "" && config.SecretKey != "": + credential = credentials.NewAccessKeyCredential(config.APIKey, config.SecretKey) + default: + return nil, fmt.Errorf("alicloud: ram role or credentials missing") } + conf := sdk.NewConfig().WithTimeout(config.HTTPTimeout) + client, err := alidns.NewClientWithOptions(config.RegionID, conf, credential) if err != nil { return nil, fmt.Errorf("alicloud: credentials failed: %w", err) diff --git a/providers/dns/alidns/alidns.toml b/providers/dns/alidns/alidns.toml index 6988dd44..7b6c08b7 100644 --- a/providers/dns/alidns/alidns.toml +++ b/providers/dns/alidns/alidns.toml @@ -5,13 +5,20 @@ Code = "alidns" Since = "v1.1.0" Example = ''' +# Setup using instance RAM role +ALICLOUD_RAM_ROLE=lego \ +lego --email myemail@example.com --dns alidns --domains my.example.org run + +# Or, using credentials ALICLOUD_ACCESS_KEY=abcdefghijklmnopqrstuvwx \ -ALICLOUD_SECRET_KEY=xxxxxxx \ +ALICLOUD_SECRET_KEY=your-secret-key \ +ALICLOUD_SECURITY_TOKEN=your-sts-token \ lego --email myemail@example.com --dns alidns --domains my.example.org run ''' [Configuration] [Configuration.Credentials] + ALICLOUD_RAM_ROLE = "Your instance RAM role (https://www.alibabacloud.com/help/doc-detail/54579.htm)" ALICLOUD_ACCESS_KEY = "Access key ID" ALICLOUD_SECRET_KEY = "Access Key secret" ALICLOUD_SECURITY_TOKEN = "STS Security Token (optional)" diff --git a/providers/dns/alidns/alidns_test.go b/providers/dns/alidns/alidns_test.go index 1a38dbfe..48799781 100644 --- a/providers/dns/alidns/alidns_test.go +++ b/providers/dns/alidns/alidns_test.go @@ -12,7 +12,8 @@ const envDomain = envNamespace + "DOMAIN" var envTest = tester.NewEnvTest( EnvAccessKey, - EnvSecretKey). + EnvSecretKey, + EnvRAMRole). WithDomain(envDomain) func TestNewDNSProvider(t *testing.T) { @@ -28,6 +29,12 @@ func TestNewDNSProvider(t *testing.T) { EnvSecretKey: "456", }, }, + { + desc: "success (RAM role)", + envVars: map[string]string{ + EnvRAMRole: "LegoInstanceRole", + }, + }, { desc: "missing credentials", envVars: map[string]string{ @@ -78,6 +85,7 @@ func TestNewDNSProvider(t *testing.T) { func TestNewDNSProviderConfig(t *testing.T) { testCases := []struct { desc string + ramRole string apiKey string secretKey string expected string @@ -87,19 +95,23 @@ func TestNewDNSProviderConfig(t *testing.T) { apiKey: "123", secretKey: "456", }, + { + desc: "success", + ramRole: "LegoInstanceRole", + }, { desc: "missing credentials", - expected: "alicloud: credentials missing", + expected: "alicloud: ram role or credentials missing", }, { desc: "missing api key", secretKey: "456", - expected: "alicloud: credentials missing", + expected: "alicloud: ram role or credentials missing", }, { desc: "missing secret key", apiKey: "123", - expected: "alicloud: credentials missing", + expected: "alicloud: ram role or credentials missing", }, } @@ -108,6 +120,7 @@ func TestNewDNSProviderConfig(t *testing.T) { config := NewDefaultConfig() config.APIKey = test.apiKey config.SecretKey = test.secretKey + config.RAMRole = test.ramRole p, err := NewDNSProviderConfig(config)