mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
allow remote service creds to be configured using env
Introduces a new method of configuring token service credentials using a host-specific environment variable. This configuration was previously possible using the [terraform-credentials-env](https://github.com/apparentlymart/terraform-credentials-env) credentials helper. This new method is now consulted first, as it is seen to be the most proximate source of credentials before CLI configuration while still falling back to the credentials helper.
This commit is contained in:
parent
714740454e
commit
307326fa3a
@ -8,6 +8,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
@ -113,6 +114,29 @@ func (c *Config) credentialsSource(helperType string, helper svcauth.Credentials
|
||||
}
|
||||
}
|
||||
|
||||
// hostCredentialsFromEnv returns a token credential by searching for a hostname-specific
|
||||
// environment variable. If the variable based on the hostname is not defined, nil is returned.
|
||||
// Variable names must have dot characters translated to underscores, which are normally not
|
||||
// allowed in DNS names. For example, token credentials for app.terraform.io should be set
|
||||
// in the variable named TF_TOKEN_app_terraform_io. Internationalized hostnames containing
|
||||
// non-ASCII characters expressed as variable names are expected to be in the "comparison" form,
|
||||
// such as "TF_TOKEN_xn--caf-dma_fr" for the hostname "café.fr"
|
||||
func hostCredentialsFromEnv(host svchost.Hostname) svcauth.HostCredentials {
|
||||
if len(host) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert dots to underscores when looking for environment configuration for a specific host.
|
||||
// DNS names do not allow underscore characters so this is unambiguous.
|
||||
variableName := fmt.Sprintf("TF_TOKEN_%s", strings.ReplaceAll(host.String(), ".", "_"))
|
||||
|
||||
if token, ok := os.LookupEnv(variableName); ok {
|
||||
return svcauth.HostCredentialsToken(token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CredentialsSource is an implementation of svcauth.CredentialsSource
|
||||
// that can read and write the CLI configuration, and possibly also delegate
|
||||
// to a credentials helper when configured.
|
||||
@ -153,11 +177,18 @@ type CredentialsSource struct {
|
||||
var _ svcauth.CredentialsSource = (*CredentialsSource)(nil)
|
||||
|
||||
func (s *CredentialsSource) ForHost(host svchost.Hostname) (svcauth.HostCredentials, error) {
|
||||
// The first order of precedence for credentials is a host-specific environment variable
|
||||
if envCreds := hostCredentialsFromEnv(host); envCreds != nil {
|
||||
return envCreds, nil
|
||||
}
|
||||
|
||||
// Then, any credentials block present in the CLI config
|
||||
v, ok := s.configured[host]
|
||||
if ok {
|
||||
return svcauth.HostCredentialsFromObject(v), nil
|
||||
}
|
||||
|
||||
// And finally, the credentials helper
|
||||
if s.helper != nil {
|
||||
return s.helper.ForHost(host)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform-svchost"
|
||||
svchost "github.com/hashicorp/terraform-svchost"
|
||||
svcauth "github.com/hashicorp/terraform-svchost/auth"
|
||||
)
|
||||
|
||||
@ -83,6 +83,53 @@ func TestCredentialsForHost(t *testing.T) {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
})
|
||||
t.Run("set in environment", func(t *testing.T) {
|
||||
envName := "TF_TOKEN_configured_example_com"
|
||||
t.Cleanup(func() {
|
||||
os.Unsetenv(envName)
|
||||
})
|
||||
|
||||
expectedToken := "configured-by-env"
|
||||
os.Setenv(envName, expectedToken)
|
||||
|
||||
creds, err := credSrc.ForHost(svchost.Hostname("configured.example.com"))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if creds == nil {
|
||||
t.Fatal("no credentials found")
|
||||
}
|
||||
|
||||
if got := creds.Token(); got != expectedToken {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", got, expectedToken)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("punycode name set in environment", func(t *testing.T) {
|
||||
envName := "TF_TOKEN_env_xn--eckwd4c7cu47r2wf_com"
|
||||
t.Cleanup(func() {
|
||||
os.Unsetenv(envName)
|
||||
})
|
||||
|
||||
expectedToken := "configured-by-env"
|
||||
os.Setenv(envName, expectedToken)
|
||||
|
||||
hostname, _ := svchost.ForComparison("env.ドメイン名例.com")
|
||||
creds, err := credSrc.ForHost(hostname)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if creds == nil {
|
||||
t.Fatal("no credentials found")
|
||||
}
|
||||
|
||||
if got := creds.Token(); got != expectedToken {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", got, expectedToken)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCredentialsStoreForget(t *testing.T) {
|
||||
|
@ -115,12 +115,22 @@ is available at multiple hostnames, use only one of them consistently.
|
||||
Terraform Cloud responds to API calls at both its current hostname
|
||||
`app.terraform.io`, and its historical hostname `atlas.hashicorp.com`.
|
||||
|
||||
### Credentials Helpers
|
||||
### Environment Variable Credentials
|
||||
|
||||
If you would prefer not to store your API tokens directly in the CLI
|
||||
configuration as described in the previous section, you can optionally instruct
|
||||
Terraform to use a different credentials storage mechanism by configuring a
|
||||
special kind of plugin program called a _credentials helper_.
|
||||
configuration, you may use a host-specific environment variable. For example,
|
||||
the value of a variable named `TF_TOKEN_app_terraform_io` will be used as an
|
||||
Authorization: Bearer token whenever making service requests to the hostname
|
||||
"app.terraform.io".
|
||||
|
||||
Internationalized domain names should be converted to their [punycode equivalent](https://www.charset.org/punycode)
|
||||
with an ACE prefix when used as a variable name. For example, token credentials
|
||||
for 例えば.com should be set in a variable called `TF_TOKEN_xn--r8j3dr99h_com`.
|
||||
|
||||
### Credentials Helpers
|
||||
|
||||
Finally, you may instruct Terraform to use a different credentials storage mechanism
|
||||
by configuring a special kind of plugin program called a _credentials helper_.
|
||||
|
||||
```hcl
|
||||
credentials_helper "example" {
|
||||
|
@ -90,8 +90,8 @@ At present, the following service identifiers are in use:
|
||||
## Authentication
|
||||
|
||||
If credentials for the given hostname are available in
|
||||
[the CLI config](/cli/config/config-file) then they will be included
|
||||
in the request for the discovery document.
|
||||
[the CLI config, by a credential helper, or using a host-specific environment variable](/cli/config/config-file#Credentials)
|
||||
then they will be included in the request for the discovery document.
|
||||
|
||||
The credentials may also be provided to endpoints declared in the discovery
|
||||
document, depending on the requirements of the service in question.
|
||||
|
Loading…
Reference in New Issue
Block a user