Skip to content

Commit

Permalink
Refactor Terraform credential loading (#44037)
Browse files Browse the repository at this point in the history
* Refactor Terraform credential loading

* Warn about expiry

* kip expired credentials

* fixup! kip expired credentials

* Use constants everywhere + add godocs

* fixup! Use constants everywhere + add godocs

* Address marco's feedback

* fixup! Address marco's feedback

* tidy go mod

* lint

* re-render TF docs
  • Loading branch information
hugoShaka committed Aug 2, 2024
1 parent af921d6 commit 650f739
Show file tree
Hide file tree
Showing 7 changed files with 807 additions and 239 deletions.
59 changes: 59 additions & 0 deletions api/client/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,62 @@ func (d *DynamicIdentityFileCreds) Expiry() (time.Time, bool) {

return x509Cert.NotAfter, true
}

// KeyPair returns a Credential give a TLS key, certificate and CA certificates PEM-encoded.
// It behaves live LoadKeyPair except it doesn't read the TLS material from a file.
// This is useful when key and certs are not on the disk (e.g. environment variables).
// This should be preferred over manually building a tls.Config and calling LoadTLS
// as Credentials returned by KeyPair can report their expiry, which allows to warn
// the user in case of expired certificates.
func KeyPair(certPEM, keyPEM, caPEM []byte) (Credentials, error) {
if len(certPEM) == 0 {
return nil, trace.BadParameter("missing certificate PEM data")
}
if len(keyPEM) == 0 {
return nil, trace.BadParameter("missing private key PEM data")
}
return &staticKeypairCreds{
certPEM: certPEM,
keyPEM: keyPEM,
caPEM: caPEM,
}, nil
}

// staticKeypairCreds uses keypair certificates to provide client credentials.
type staticKeypairCreds struct {
certPEM []byte
keyPEM []byte
caPEM []byte
}

// TLSConfig returns TLS configuration.
func (c *staticKeypairCreds) TLSConfig() (*tls.Config, error) {
cert, err := keys.X509KeyPair(c.certPEM, c.keyPEM)
if err != nil {
return nil, trace.Wrap(err)
}

pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(c.caPEM); !ok {
return nil, trace.BadParameter("invalid TLS CA cert PEM")
}

return configureTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: pool,
}), nil
}

// SSHClientConfig returns SSH configuration.
func (c *staticKeypairCreds) SSHClientConfig() (*ssh.ClientConfig, error) {
return nil, trace.NotImplemented("no ssh config")
}

// Expiry returns the credential expiry.
func (c *staticKeypairCreds) Expiry() (time.Time, bool) {
cert, _, err := keys.X509Certificate(c.certPEM)
if err != nil {
return time.Time{}, false
}
return cert.NotAfter, true
}
26 changes: 26 additions & 0 deletions api/client/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,32 @@ func TestLoadKeyPair(t *testing.T) {
require.False(t, ok, "expiry should be unknown on a broken credential")
}

func TestKeyPair(t *testing.T) {
t.Parallel()

// Load expected tls.Config.
expectedTLSConfig := getExpectedTLSConfig(t)

// Load key pair from disk.
creds, err := KeyPair(tlsCert, keyPEM, tlsCACert)
require.NoError(t, err)

// Build tls.Config and compare to expected tls.Config.
tlsConfig, err := creds.TLSConfig()
require.NoError(t, err)
requireEqualTLSConfig(t, expectedTLSConfig, tlsConfig)

// Load invalid keypairs.
invalidIdentityCreds, err := KeyPair([]byte("invalid_cert"), []byte("invalid_key"), []byte("invalid_ca_cert"))
require.NoError(t, err)
_, err = invalidIdentityCreds.TLSConfig()
require.Error(t, err)

// Load missing keypairs
_, err = KeyPair(nil, nil, nil)
require.Error(t, err)
}

func TestLoadProfile(t *testing.T) {
t.Parallel()
profileName := "proxy.example.com"
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/reference/terraform-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ This auth method has the following limitations:

### Optional

- `addr` (String) host:port where Teleport Auth Service is running. This can also be set with the environment variable `TF_TELEPORT_ADDR`.
- `addr` (String) host:port of the Teleport address. This can be the Teleport Proxy Service address (port 443 or 4080) or the Teleport Auth Service address (port 3025). This can also be set with the environment variable `TF_TELEPORT_ADDR`.
- `cert_base64` (String) Base64 encoded TLS auth certificate. This can also be set with the environment variable `TF_TELEPORT_CERT_BASE64`.
- `cert_path` (String) Path to Teleport auth certificate file. This can also be set with the environment variable `TF_TELEPORT_CERT`.
- `dial_timeout_duration` (String) DialTimeout sets timeout when trying to connect to the server. This can also be set with the environment variable `TF_TELEPORT_DIAL_TIMEOUT_DURATION`.
Expand Down
3 changes: 2 additions & 1 deletion integrations/terraform/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ require (
google.golang.org/protobuf v1.34.1
)

require github.com/hashicorp/terraform-plugin-log v0.9.0

require (
cloud.google.com/go v0.112.2 // indirect
cloud.google.com/go/auth v0.3.0 // indirect
Expand Down Expand Up @@ -206,7 +208,6 @@ require (
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.21.0 // indirect
github.com/hashicorp/terraform-json v0.22.1 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.1 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
Expand Down
Loading

0 comments on commit 650f739

Please sign in to comment.