Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,8 @@
"SVIDs",
"Ghostunnel",
"Linkerd",
"Istio"
"Istio",
"mydb"
],
"flagWords": [
"hte"
Expand Down
21 changes: 20 additions & 1 deletion docs/pages/machine-id/workload-identity/best-practices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,23 @@ yourself.

One such proxy is [Ghostunnel](https://github.com/ghostunnel/ghostunnel).


## X509 SVID Subject

When the X509 SVIDs are issued by Teleport Workload Identity, the subject
distinguished name of the certificate is determined by the following criteria:

- If no DNS SANs have been requested, the subject is unset.
- If DNS SANs have been requested, the first DNS SAN is set as the subject
common name.

This behavior exists to support interoperability with legacy systems which are
not able to parse DNS SANs or which are not SPIFFE aware.

An example of one such legacy system is Postgres. Postgres supports client
authentication using certificates, but only allows the common name to be used
to determine which database user access should be granted to. To integrate
Teleport Workload Identity with Postgres, you can issue X509 SVIDs with a DNS
SAN which can then be mapped to database user. For example, you could issue
a certificate with a DNS SAN of `myuser.mydb.db-access.example.com`. The
behavior described above will then set the common name to this DNS SAN, and you
can then configure Postgres to map this common name to `myuser`.
9 changes: 9 additions & 0 deletions lib/auth/machineid/machineidv1/workload_identity_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,15 @@ func signx509SVID(
IPAddresses: ipSANS,
}

// For legacy compatibility, we set the subject common name to the first
// DNS SAN. This allows interoperability with non-SPIFFE aware clients that
// trust the CA, or interoperability with servers like Postgres which can
// only inspect the common name when making authz/authn decisions.
// Eventually, we may wish to make this behavior more configurable.
if len(dnsSANs) > 0 {
template.Subject.CommonName = dnsSANs[0]
}

certBytes, err := x509.CreateCertificate(
rand.Reader, template, ca.Cert, pubKey, ca.Signer,
)
Expand Down
13 changes: 10 additions & 3 deletions lib/auth/machineid/machineidv1/workload_identity_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ func TestWorkloadIdentityService_SignX509SVIDs(t *testing.T) {
PublicKey: pubBytes,
Hint: "llamas",
Ttl: durationpb.New(30 * time.Minute),
DnsSans: []string{"foo.alpha.example.com"},
IpSans: []string{"10.42.42.42"},
DnsSans: []string{
"foo.alpha.example.com",
"bar.alpha.example.com",
},
IpSans: []string{"10.42.42.42"},
},
},
},
Expand Down Expand Up @@ -143,8 +146,9 @@ func TestWorkloadIdentityService_SignX509SVIDs(t *testing.T) {
require.Equal(t, wantSPIFFEID, cert.URIs[0].String())
// 2: An X.509 SVID MAY contain any number of other SAN field types, including DNS SANs.
// Here we validate against what was requested
require.Len(t, cert.DNSNames, 1)
require.Len(t, cert.DNSNames, 2)
require.Equal(t, "foo.alpha.example.com", cert.DNSNames[0])
require.Equal(t, "bar.alpha.example.com", cert.DNSNames[1])
require.Len(t, cert.IPAddresses, 1)
require.Equal(t, "10.42.42.42", cert.IPAddresses[0].String())
// 4.1: leaf certificates MUST set the cA field to false.
Expand All @@ -160,6 +164,9 @@ func TestWorkloadIdentityService_SignX509SVIDs(t *testing.T) {
// 4.4: When included, fields id-kp-serverAuth and id-kp-clientAuth MUST be set.
require.Contains(t, cert.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
require.Contains(t, cert.ExtKeyUsage, x509.ExtKeyUsageClientAuth)

// Check that the Common Name is set to the first DNS SAN.
require.Equal(t, "foo.alpha.example.com", cert.Subject.CommonName)
},
},
{
Expand Down