-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add min-tls and ciphers-suite application options
Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com> fix typos Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com> add test of TLS configuration Signed-off-by: Oleksandr Krutko <alexander.krutko@gmail.com>
- Loading branch information
Showing
5 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
package app | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/ed25519" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/pem" | ||
"fmt" | ||
"log" | ||
"math/big" | ||
"net" | ||
"net/http" | ||
"os" | ||
"path" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/cert-manager/trust-manager/cmd/trust-manager/app/options" | ||
"github.com/spf13/cobra" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
) | ||
|
||
func TestCiphersSuite(t *testing.T) { | ||
tmpDir := t.TempDir() | ||
setupWebHookServer(tmpDir) | ||
|
||
t.Run("TLS 1.2 client connect to TLS 1.2 server, ciphers suite supported", func(t *testing.T) { | ||
client := http.Client{ | ||
Transport: &http.Transport{ | ||
ForceAttemptHTTP2: false, | ||
TLSClientConfig: &tls.Config{ | ||
InsecureSkipVerify: true, | ||
MinVersion: tls.VersionTLS12, | ||
MaxVersion: tls.VersionTLS12, | ||
CipherSuites: []uint16{ | ||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | ||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
_, err := client.Get(fmt.Sprintf("https://%s:%d", "localhost", 6443)) | ||
if err != nil { | ||
t.Fatalf("error to connect to webhook server, %s\n", err) | ||
} | ||
}) | ||
|
||
t.Run("TLS 1.2 client connect to TLS 1.2 server, ciphers suite unsupported", func(t *testing.T) { | ||
client := http.Client{ | ||
Transport: &http.Transport{ | ||
ForceAttemptHTTP2: false, | ||
TLSClientConfig: &tls.Config{ | ||
InsecureSkipVerify: true, | ||
MinVersion: tls.VersionTLS12, | ||
MaxVersion: tls.VersionTLS12, | ||
CipherSuites: []uint16{ | ||
tls.TLS_RSA_WITH_AES_256_CBC_SHA, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
_, err := client.Get(fmt.Sprintf("https://%s:%d", "localhost", 6443)) | ||
if err != nil { | ||
return | ||
} | ||
}) | ||
} | ||
|
||
func setupWebHookServer(tmpDir string) error { | ||
err := genPrivAndCert(tmpDir) | ||
if err != nil { | ||
fmt.Printf("unable to generate certificate and privkey %s\n", err) | ||
return err | ||
} | ||
|
||
args := []string{ | ||
"--readiness-probe-port=9443", | ||
"--readiness-probe-path=/readyz", | ||
"--leader-election-lease-duration=15s", | ||
"--leader-election-renew-deadline=10s", | ||
"--metrics-port=9402", | ||
"--trust-namespace=cert-manager", | ||
"--secret-targets-enabled=false", | ||
"--filter-expired-certificates=false", | ||
"--webhook-host=0.0.0.0", | ||
"--webhook-port=6443", | ||
"--min-tls=tls12", | ||
"--ciphers-suite=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_GCM_SHA256", | ||
} | ||
|
||
opts := options.New() | ||
|
||
cmd := &cobra.Command{ | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
srv := webhook.NewServer(webhook.Options{ | ||
CertDir: tmpDir, | ||
Port: opts.Port, | ||
|
||
TLSOpts: []func(*tls.Config){ | ||
func(cfg *tls.Config) { | ||
cfg.MinVersion = getMinTLSVersionByName(opts.MinTLSVersion) | ||
|
||
var cipherSuiteList []*tls.CipherSuite | ||
cipherSuiteList = append(cipherSuiteList, tls.CipherSuites()...) | ||
cipherSuiteList = append(cipherSuiteList, tls.InsecureCipherSuites()...) | ||
cfg.CipherSuites = getCipherSuiteByNames(opts.CiphersSuite, cipherSuiteList...) | ||
}, | ||
}, | ||
}) | ||
|
||
if err != nil { | ||
cmd.PrintErrf("error creating webhook server %s\n", err) | ||
return err | ||
} | ||
|
||
go srv.Start(cmd.Context()) | ||
|
||
return nil | ||
}, | ||
} | ||
cmd.SetArgs(args) | ||
opts.Prepare(cmd) | ||
err = cmd.Execute() | ||
if err != nil { | ||
cmd.PrintErrf("error: %v\n", err) | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
// This implementation was taken as is from | ||
// https://go.dev/src/crypto/tls/generate_cert.go source file | ||
func genPrivAndCert(dir string) error { | ||
var priv any | ||
var err error | ||
priv, err = rsa.GenerateKey(rand.Reader, 2048) | ||
if err != nil { | ||
return fmt.Errorf("Failed to generate private key: %v", err) | ||
} | ||
|
||
keyUsage := x509.KeyUsageDigitalSignature | ||
if _, isRSA := priv.(*rsa.PrivateKey); isRSA { | ||
keyUsage |= x509.KeyUsageKeyEncipherment | ||
} | ||
|
||
var notBefore = time.Now() | ||
notAfter := notBefore.Add(365 * 24 * time.Hour) | ||
|
||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | ||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | ||
if err != nil { | ||
return fmt.Errorf("Failed to generate serial number: %v", err) | ||
} | ||
|
||
template := x509.Certificate{ | ||
SerialNumber: serialNumber, | ||
Subject: pkix.Name{ | ||
Organization: []string{"Acme Co"}, | ||
}, | ||
NotBefore: notBefore, | ||
NotAfter: notAfter, | ||
|
||
KeyUsage: keyUsage, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||
BasicConstraintsValid: true, | ||
} | ||
|
||
hosts := strings.Split("localhost, 127.0.0.1", ",") | ||
for _, h := range hosts { | ||
if ip := net.ParseIP(h); ip != nil { | ||
template.IPAddresses = append(template.IPAddresses, ip) | ||
} else { | ||
template.DNSNames = append(template.DNSNames, h) | ||
} | ||
} | ||
|
||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) | ||
if err != nil { | ||
return fmt.Errorf("Failed to create certificate: %v", err) | ||
} | ||
|
||
certOut, err := os.Create(path.Join(dir, "tls.crt")) | ||
if err != nil { | ||
return fmt.Errorf("Failed to open cert.pem for writing: %v", err) | ||
} | ||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { | ||
return fmt.Errorf("Failed to write data to tls.crt: %v", err) | ||
} | ||
if err := certOut.Close(); err != nil { | ||
return fmt.Errorf("Error closing tls.crt: %v", err) | ||
} | ||
log.Print("wrote tls.crt\n") | ||
|
||
keyOut, err := os.OpenFile(path.Join(dir, "tls.key"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | ||
if err != nil { | ||
return fmt.Errorf("Failed to open tls.key for writing: %v", err) | ||
} | ||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv) | ||
if err != nil { | ||
return fmt.Errorf("Unable to marshal private key: %v", err) | ||
} | ||
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { | ||
return fmt.Errorf("Failed to write data to tls.key: %v", err) | ||
} | ||
if err := keyOut.Close(); err != nil { | ||
return fmt.Errorf("Error closing tls.key: %v", err) | ||
} | ||
log.Print("wrote tls.key\n") | ||
return nil | ||
} | ||
|
||
func publicKey(priv any) any { | ||
switch k := priv.(type) { | ||
case *rsa.PrivateKey: | ||
return &k.PublicKey | ||
case *ecdsa.PrivateKey: | ||
return &k.PublicKey | ||
case ed25519.PrivateKey: | ||
return k.Public().(ed25519.PublicKey) | ||
default: | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.