Skip to content

Commit

Permalink
configurable key algorithms for proxy to database agent certs (#43329)
Browse files Browse the repository at this point in the history
* configurable algorithms for proxy to database agent certs

* rfd edit

* use v1 suite in test

* fix typo

* fix bad merge
  • Loading branch information
nklaassen authored Jun 26, 2024
1 parent 1acd9cb commit a48c4f7
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 56 deletions.
14 changes: 10 additions & 4 deletions api/utils/keys/privatekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,17 @@ func (k *PrivateKey) PrivateKeyPEM() []byte {
return k.keyPEM
}

// TLSCertificate parses the given TLS certificate(s) paired with the private key
// to rerturn a tls.Certificate, ready to be used in a TLS handshake.
// TLSCertificate parses the given TLS certificate(s) paired with the private
// key to return a tls.Certificate, ready to be used in a TLS handshake.
func (k *PrivateKey) TLSCertificate(certPEMBlock []byte) (tls.Certificate, error) {
return TLSCertificateForSigner(k.Signer, certPEMBlock)
}

// TLSCertificate parses the given TLS certificate(s) paired with the given
// signer to return a tls.Certificate, ready to be used in a TLS handshake.
func TLSCertificateForSigner(signer crypto.Signer, certPEMBlock []byte) (tls.Certificate, error) {
cert := tls.Certificate{
PrivateKey: k.Signer,
PrivateKey: signer,
}

var skippedBlockTypes []string
Expand Down Expand Up @@ -125,7 +131,7 @@ func (k *PrivateKey) TLSCertificate(certPEMBlock []byte) (tls.Certificate, error
return tls.Certificate{}, trace.Wrap(err)
}

if keyPub, ok := k.Public().(cryptoPublicKeyI); !ok {
if keyPub, ok := signer.Public().(cryptoPublicKeyI); !ok {
return tls.Certificate{}, trace.BadParameter("private key does not contain a valid public key")
} else if !keyPub.Equal(x509Cert.PublicKey) {
return tls.Certificate{}, trace.BadParameter("private key does not match certificate's public key")
Expand Down
92 changes: 50 additions & 42 deletions lib/cryptosuites/suites.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,11 @@ const (
// SPIFFECAJWT represents the JWT key for the spiffe CA.
SPIFFECAJWT

// New key purposes should be added here.
// ProxyToDatabaseAgent represents keys used by the Proxy to dial the
// Database agent over a reverse tunnel.
ProxyToDatabaseAgent

// TODO(nklaassen): define subject key purposes. Currently only CA key purposes are defined above.
// TODO(nklaassen): define remaining key purposes.

// keyPurposeMax is 1 greater than the last valid key purpose, used to test that all values less than this
// are valid for each suite.
Expand Down Expand Up @@ -134,45 +136,50 @@ var (
SAMLIdPCATLS: RSA2048,
SPIFFECATLS: RSA2048,
SPIFFECAJWT: RSA2048,
// TODO(nklaassen): subject key purposes.
// We could consider updating this algorithm even in the legacy suite, only database agents need to
// accept these connections and they have never restricted algorithm support.
ProxyToDatabaseAgent: RSA2048,
// TODO(nklaassen): define remaining key purposes.
}

// balancedV1 strikes a balance between security, compatibility, and
// performance. It uses ECDSA256, Ed25591, and 2048-bit RSA. It is not
// completely implemented yet.
balancedV1 = suite{
UserCATLS: ECDSAP256,
UserCASSH: Ed25519,
HostCATLS: ECDSAP256,
HostCASSH: Ed25519,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: Ed25519,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
// TODO(nklaassen): subject key purposes.
UserCATLS: ECDSAP256,
UserCASSH: Ed25519,
HostCATLS: ECDSAP256,
HostCASSH: Ed25519,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: Ed25519,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
ProxyToDatabaseAgent: ECDSAP256,
// TODO(nklaassen): define remaining key purposes.
}

// fipsv1 is an algorithm suite tailored for FIPS compliance. It is based on
// the balancedv1 suite but replaces all instances of Ed25519 with ECDSA on
// the NIST P256 curve. It is not completely implemented yet.
fipsv1 = suite{
UserCATLS: ECDSAP256,
UserCASSH: ECDSAP256,
HostCATLS: ECDSAP256,
HostCASSH: ECDSAP256,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: ECDSAP256,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
// TODO(nklaassen): subject key purposes.
UserCATLS: ECDSAP256,
UserCASSH: ECDSAP256,
HostCATLS: ECDSAP256,
HostCASSH: ECDSAP256,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: ECDSAP256,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
ProxyToDatabaseAgent: ECDSAP256,
// TODO(nklaassen): define remaining key purposes.
}

// hsmv1 in an algorithm suite tailored for clusters using an HSM or KMS
Expand All @@ -181,19 +188,20 @@ var (
// only*. It is also valid to use the legacy or fipsv1 suites if your
// cluster uses an HSM or KMS. It is not completely implemented yet.
hsmv1 = suite{
UserCATLS: ECDSAP256,
UserCASSH: ECDSAP256,
HostCATLS: ECDSAP256,
HostCASSH: ECDSAP256,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: ECDSAP256,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
// TODO(nklaassen): subject key purposes.
UserCATLS: ECDSAP256,
UserCASSH: ECDSAP256,
HostCATLS: ECDSAP256,
HostCASSH: ECDSAP256,
DatabaseCATLS: RSA2048,
DatabaseClientCATLS: RSA2048,
OpenSSHCASSH: ECDSAP256,
JWTCAJWT: ECDSAP256,
OIDCIdPCAJWT: ECDSAP256,
SAMLIdPCATLS: ECDSAP256,
SPIFFECATLS: ECDSAP256,
SPIFFECAJWT: ECDSAP256,
ProxyToDatabaseAgent: ECDSAP256,
// TODO(nklaassen): define remaining key purposes.
}

allSuites = map[types.SignatureAlgorithmSuite]suite{
Expand Down
5 changes: 4 additions & 1 deletion lib/srv/db/access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2240,7 +2240,10 @@ func setupTestContext(ctx context.Context, t testing.TB, withDatabases ...withDa
authServer, err := auth.NewTestAuthServer(auth.TestAuthServerConfig{
Clock: testCtx.clock,
ClusterName: testCtx.clusterName,
Dir: t.TempDir(),
AuthPreferenceSpec: &types.AuthPreferenceSpecV2{
SignatureAlgorithmSuite: types.SignatureAlgorithmSuite_SIGNATURE_ALGORITHM_SUITE_BALANCED_V1,
},
Dir: t.TempDir(),
})
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, authServer.Close()) })
Expand Down
7 changes: 4 additions & 3 deletions lib/srv/db/proxyserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
apiutils "github.com/gravitational/teleport/api/utils"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/auth/authclient"
"github.com/gravitational/teleport/lib/auth/native"
"github.com/gravitational/teleport/lib/authz"
"github.com/gravitational/teleport/lib/cryptosuites"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/observability/metrics"
Expand Down Expand Up @@ -616,7 +617,7 @@ func (s *ProxyServer) getDatabaseServers(ctx context.Context, identity tlsca.Ide
func (s *ProxyServer) getConfigForServer(ctx context.Context, identity tlsca.Identity, server types.DatabaseServer) (*tls.Config, error) {
defer observeLatency(tlsConfigTime.With(getLabelsFromDB(server.GetDatabase())))()

privateKey, err := native.GeneratePrivateKey()
privateKey, err := cryptosuites.GenerateKey(ctx, s.cfg.AccessPoint, cryptosuites.ProxyToDatabaseAgent)
if err != nil {
return nil, trace.Wrap(err)
}
Expand All @@ -637,7 +638,7 @@ func (s *ProxyServer) getConfigForServer(ctx context.Context, identity tlsca.Ide
return nil, trace.Wrap(err)
}

cert, err := privateKey.TLSCertificate(response.Cert)
cert, err := keys.TLSCertificateForSigner(privateKey, response.Cert)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
32 changes: 26 additions & 6 deletions rfd/0136-modern-signature-algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,16 @@ The following key types will be used when the configured algorithm suite is
* TLS: ECDSA with NIST P-256 (X.509 cert signed by host CA)
* OpenSSH hosts
* SSH: Ed25519 (SSH cert signed by host CA)
* User certs for "Agentless" SSH connections
* proxy -> Agentless/OpenSSH certs
* SSH: Ed25519 (SSH certs signed by OpenSSH CA)
* proxy -> database agent
* 2048-bit RSA (X.509 cert signed by Database CA)
* this may be forwarded directly to the database
* ECDSA with NIST P-256 (X.509 cert signed by Host CA)
* database agent -> self-hosted database
* 2048-bit RSA (X.509 cert signed by Database CA)
* 2048-bit RSA (X.509 cert signed by Database Client CA)
* for Snowflake access this is a JWT signed by the Database Client CA
* maybe we could choose the subject key algorithm per-database
* self-hosted database
* 2048-bit RSA (X.509 cert signed by Database CA)
* this may be a JWT for Snowflake access
* windows desktop service -> RDP server
* 2048-bit RSA (X.509 cert signed by user CA)
* this is a current limitation of our rdpclient implementation, we could
Expand Down Expand Up @@ -280,7 +280,11 @@ uses: user ssh cert signing, user tls cert signing, ssh hosts trust this CA

keys: ssh, tls

uses: host ssh cert signing, host tls cert signing, ssh clients trust this CA
uses:

* signs host ssh certs
* signs host tls certs
* ssh clients trust this CA

* current/`legacy` SSH key type: 2048-bit RSA
* proposed `balanced-v1` key type: Ed25519
Expand Down Expand Up @@ -310,6 +314,22 @@ uses:

* signs (often) long-lived db cert used to authenticate db to database service
* signs short-lived proxy cert used to authenticate proxy to database service

* current/`legacy` TLS key type: 2048-bit RSA
* proposed `balanced-v1` key type: 2048-bit RSA
* proposed `fips-v1` key type: 2048-bit RSA
* proposed `hsm-v1` key type: 2048-bit RSA
* reasoning:
* some database protocols still require RSA, reduce friction by keeping it as
the default

#### Database Client CA

keys: tls

uses:

* signs short-lived certs used to authenticate db service to databases
* signs snowflake JWTs
* self-hosted databases (and Snowflake) trust this CA

Expand Down

0 comments on commit a48c4f7

Please sign in to comment.