github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/testhelpers/mtls/mtls.go (about) 1 // Copyright 2023 Gravitational, Inc 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mtls 16 17 import ( 18 "crypto/ed25519" 19 "crypto/rand" 20 "crypto/tls" 21 "crypto/x509" 22 "crypto/x509/pkix" 23 "encoding/pem" 24 "math/big" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/require" 29 30 "github.com/gravitational/teleport/api/constants" 31 "github.com/gravitational/teleport/api/utils/keys" 32 ) 33 34 type Config struct { 35 ServerTLS *tls.Config 36 ClientTLS *tls.Config 37 } 38 39 // NewConfig returns an mTLS config. 40 func NewConfig(t *testing.T) *Config { 41 t.Helper() 42 43 caKey, caCert := generateCA(t) 44 serverTLS := generateChildTLSConfigFromCA(t, caKey, caCert) 45 clientTLS := generateChildTLSConfigFromCA(t, caKey, caCert) 46 clientTLS.ServerName = constants.APIDomain 47 48 return &Config{ 49 ServerTLS: serverTLS, 50 ClientTLS: clientTLS, 51 } 52 } 53 54 func generateCA(t *testing.T) (*keys.PrivateKey, *x509.Certificate) { 55 t.Helper() 56 57 caPub, caPriv, err := ed25519.GenerateKey(rand.Reader) 58 require.NoError(t, err) 59 caKey, err := keys.NewPrivateKey(caPriv, nil) 60 require.NoError(t, err) 61 62 // Create a self signed certificate. 63 64 notBefore := time.Now() 65 notAfter := notBefore.Add(time.Minute) 66 entity := pkix.Name{ 67 Organization: []string{"teleport"}, 68 CommonName: "localhost", 69 } 70 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 71 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 72 require.NoError(t, err) 73 74 template := &x509.Certificate{ 75 SerialNumber: serialNumber, 76 Issuer: entity, 77 Subject: entity, 78 NotBefore: notBefore, 79 NotAfter: notAfter, 80 KeyUsage: x509.KeyUsageCertSign, 81 IsCA: true, 82 BasicConstraintsValid: true, 83 } 84 85 caCertDER, err := x509.CreateCertificate(rand.Reader, template, template, caPub, caKey) 86 require.NoError(t, err) 87 88 x509Cert, err := x509.ParseCertificate(caCertDER) 89 require.NoError(t, err) 90 91 return caKey, x509Cert 92 } 93 94 func generateChildTLSConfigFromCA(t *testing.T, caKey *keys.PrivateKey, caCert *x509.Certificate) *tls.Config { 95 t.Helper() 96 97 pub, priv, err := ed25519.GenerateKey(rand.Reader) 98 require.NoError(t, err) 99 100 key, err := keys.NewPrivateKey(priv, nil) 101 require.NoError(t, err) 102 103 // Create a certificate signed by the CA. 104 105 notBefore := time.Now() 106 notAfter := notBefore.Add(time.Minute) 107 entity := pkix.Name{ 108 Organization: []string{"teleport"}, 109 CommonName: "localhost", 110 } 111 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 112 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 113 require.NoError(t, err) 114 115 template := &x509.Certificate{ 116 SerialNumber: serialNumber, 117 Subject: entity, 118 NotBefore: notBefore, 119 NotAfter: notAfter, 120 KeyUsage: x509.KeyUsageDigitalSignature, 121 BasicConstraintsValid: true, 122 DNSNames: []string{constants.APIDomain}, 123 } 124 125 certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, pub, caKey) 126 require.NoError(t, err) 127 certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) 128 129 tlsCert, err := key.TLSCertificate(certPEM) 130 require.NoError(t, err) 131 132 pool := x509.NewCertPool() 133 pool.AddCert(caCert) 134 135 return &tls.Config{ 136 Certificates: []tls.Certificate{tlsCert}, 137 RootCAs: pool, 138 } 139 }