Skip to content

Commit

Permalink
Implement private key size annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
snorwin committed Nov 27, 2023
1 parent a4f925c commit 6762f17
Show file tree
Hide file tree
Showing 2 changed files with 277 additions and 5 deletions.
57 changes: 52 additions & 5 deletions internal/controller/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
ReasonIssuing = `Issuing`
ReasonInvalidKey = `InvalidKey`
ReasonInvalidPrivateKeyAlgorithm = `InvalidPrivateKeyAlgorithm`
ReasonInvalidPrivateKeySize = `InvalidPrivateKeySize`
ReasonInvalidValue = `InvalidValue`
ReasonInternalReconcileError = `InternalReconcileError`
ReasonMissingHostname = `MissingHostname`
Expand Down Expand Up @@ -219,16 +220,35 @@ func (r *Route) generateNextPrivateKey(ctx context.Context, route *routev1.Route
if !found {
privateKeyAlgorithm = string(cmapi.RSAKeyAlgorithm)
}

var privateKeySize int
privateKeySizeStr, found := route.Annotations[cmapi.PrivateKeySizeAnnotationKey]
if found {
var err error
privateKeySize, err = strconv.Atoi(privateKeySizeStr)
if err != nil {
r.eventRecorder.Event(route, corev1.EventTypeWarning, ReasonInvalidPrivateKeySize, "invalid private key size:"+privateKeySizeStr)
return fmt.Errorf("invalid private key size, %s: %v", privateKeySizeStr, err)
}
} else {
switch privateKeyAlgorithm {
case string(cmapi.ECDSAKeyAlgorithm):
privateKeySize = utilpki.ECCurve256
case string(cmapi.RSAKeyAlgorithm):
privateKeySize = utilpki.MinRSAKeySize
}
}

var privateKey crypto.PrivateKey
var err error
switch privateKeyAlgorithm {
case string(cmapi.ECDSAKeyAlgorithm):
privateKey, err = utilpki.GenerateECPrivateKey(utilpki.ECCurve256)
privateKey, err = utilpki.GenerateECPrivateKey(privateKeySize)
if err != nil {
return fmt.Errorf("could not generate ECDSA key: %w", err)
}
case string(cmapi.RSAKeyAlgorithm):
privateKey, err = utilpki.GenerateRSAPrivateKey(utilpki.MinRSAKeySize)
privateKey, err = utilpki.GenerateRSAPrivateKey(privateKeySize)
if err != nil {
return fmt.Errorf("could not generate RSA Key: %w", err)
}
Expand Down Expand Up @@ -374,15 +394,42 @@ func (r *Route) buildNextCR(ctx context.Context, route *routev1.Route, revision
privateKeyAlgorithm = string(cmapi.RSAKeyAlgorithm)
}

var privateKeySize int
privateKeySizeStr, found := route.Annotations[cmapi.PrivateKeySizeAnnotationKey]
if found {
privateKeySize, err = strconv.Atoi(privateKeySizeStr)
if err != nil {
r.eventRecorder.Event(route, corev1.EventTypeWarning, ReasonInvalidPrivateKeySize, "invalid private key size:"+privateKeySizeStr)
return nil, fmt.Errorf("invalid private key size, %s: %v", privateKeySizeStr, err)
}
}

var signatureAlgorithm x509.SignatureAlgorithm
var publicKeyAlgorithm x509.PublicKeyAlgorithm
switch privateKeyAlgorithm {
case string(cmapi.ECDSAKeyAlgorithm):
signatureAlgorithm = x509.ECDSAWithSHA256
switch privateKeySize {
case 521:
signatureAlgorithm = x509.ECDSAWithSHA512
case 384:
signatureAlgorithm = x509.ECDSAWithSHA384
case 256:
signatureAlgorithm = x509.ECDSAWithSHA256
default:
signatureAlgorithm = x509.ECDSAWithSHA256
}
publicKeyAlgorithm = x509.ECDSA

case string(cmapi.RSAKeyAlgorithm):
signatureAlgorithm = x509.SHA256WithRSA
switch {
case privateKeySize >= 4096:
signatureAlgorithm = x509.SHA512WithRSA
case privateKeySize >= 3072:
signatureAlgorithm = x509.SHA384WithRSA
case privateKeySize >= 2048:
signatureAlgorithm = x509.SHA256WithRSA
default:
signatureAlgorithm = x509.SHA256WithRSA
}
publicKeyAlgorithm = x509.RSA

default:
Expand Down
225 changes: 225 additions & 0 deletions internal/controller/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"net"
"net/url"
"sort"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -856,6 +857,118 @@ func TestRoute_buildNextCR(t *testing.T) {
},
wantErr: nil,
},
{
name: "With ECDSA 384 private key algorithm and size annotation",
revision: 1337,
route: generateRouteStatus(&routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: "some-route",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.IsNextPrivateKeySecretLabelKey: string(ecdsaPEM),
cmapi.PrivateKeyAlgorithmAnnotationKey: string(cmapi.ECDSAKeyAlgorithm),
cmapi.PrivateKeySizeAnnotationKey: strconv.Itoa(384),
},
},
Spec: routev1.RouteSpec{
Host: "some-host.some-domain.tld",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "some-host.some-domain.tld",
Conditions: []routev1.RouteIngressCondition{
{
Type: "Admitted",
Status: "True",
},
},
},
},
},
},
true),
want: &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "some-route-",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: "1338",
},
},
Spec: cmapi.CertificateRequestSpec{
Usages: []cmapi.KeyUsage{cmapi.UsageServerAuth, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment},
Duration: &metav1.Duration{Duration: DefaultCertificateDuration},
},
},
wantCSR: &x509.CertificateRequest{
SignatureAlgorithm: x509.ECDSAWithSHA256,
PublicKeyAlgorithm: x509.ECDSA,
Subject: pkix.Name{
CommonName: "",
},
DNSNames: []string{"some-host.some-domain.tld"},
IPAddresses: []net.IP(nil),
URIs: []*url.URL(nil),
},
wantErr: nil,
},
{
name: "With ECDSA 521 private key algorithm and size annotation",
revision: 1337,
route: generateRouteStatus(&routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: "some-route",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.IsNextPrivateKeySecretLabelKey: string(ecdsaPEM),
cmapi.PrivateKeyAlgorithmAnnotationKey: string(cmapi.ECDSAKeyAlgorithm),
cmapi.PrivateKeySizeAnnotationKey: strconv.Itoa(521),
},
},
Spec: routev1.RouteSpec{
Host: "some-host.some-domain.tld",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "some-host.some-domain.tld",
Conditions: []routev1.RouteIngressCondition{
{
Type: "Admitted",
Status: "True",
},
},
},
},
},
},
true),
want: &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "some-route-",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: "1338",
},
},
Spec: cmapi.CertificateRequestSpec{
Usages: []cmapi.KeyUsage{cmapi.UsageServerAuth, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment},
Duration: &metav1.Duration{Duration: DefaultCertificateDuration},
},
},
wantCSR: &x509.CertificateRequest{
SignatureAlgorithm: x509.ECDSAWithSHA256,
PublicKeyAlgorithm: x509.ECDSA,
Subject: pkix.Name{
CommonName: "",
},
DNSNames: []string{"some-host.some-domain.tld"},
IPAddresses: []net.IP(nil),
URIs: []*url.URL(nil),
},
wantErr: nil,
},
{
name: "With RSA private key algorithm annotation",
revision: 1337,
Expand Down Expand Up @@ -911,6 +1024,118 @@ func TestRoute_buildNextCR(t *testing.T) {
},
wantErr: nil,
},
{
name: "With RSA 3072 private key algorithm and size annotation",
revision: 1337,
route: generateRouteStatus(&routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: "some-route",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.IsNextPrivateKeySecretLabelKey: string(rsaPEM),
cmapi.PrivateKeyAlgorithmAnnotationKey: string(cmapi.RSAKeyAlgorithm),
cmapi.PrivateKeySizeAnnotationKey: strconv.Itoa(3072),
},
},
Spec: routev1.RouteSpec{
Host: "some-host.some-domain.tld",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "some-host.some-domain.tld",
Conditions: []routev1.RouteIngressCondition{
{
Type: "Admitted",
Status: "True",
},
},
},
},
},
},
true),
want: &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "some-route-",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: "1338",
},
},
Spec: cmapi.CertificateRequestSpec{
Usages: []cmapi.KeyUsage{cmapi.UsageServerAuth, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment},
Duration: &metav1.Duration{Duration: DefaultCertificateDuration},
},
},
wantCSR: &x509.CertificateRequest{
SignatureAlgorithm: x509.SHA384WithRSA,
PublicKeyAlgorithm: x509.RSA,
Subject: pkix.Name{
CommonName: "",
},
DNSNames: []string{"some-host.some-domain.tld"},
IPAddresses: []net.IP(nil),
URIs: []*url.URL(nil),
},
wantErr: nil,
},
{
name: "With RSA 3072 private key algorithm and size annotation",
revision: 1337,
route: generateRouteStatus(&routev1.Route{
ObjectMeta: metav1.ObjectMeta{
Name: "some-route",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.IsNextPrivateKeySecretLabelKey: string(rsaPEM),
cmapi.PrivateKeyAlgorithmAnnotationKey: string(cmapi.RSAKeyAlgorithm),
cmapi.PrivateKeySizeAnnotationKey: strconv.Itoa(4096),
},
},
Spec: routev1.RouteSpec{
Host: "some-host.some-domain.tld",
},
Status: routev1.RouteStatus{
Ingress: []routev1.RouteIngress{
{
Host: "some-host.some-domain.tld",
Conditions: []routev1.RouteIngressCondition{
{
Type: "Admitted",
Status: "True",
},
},
},
},
},
},
true),
want: &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
GenerateName: "some-route-",
Namespace: "some-namespace",
Annotations: map[string]string{
cmapi.CertificateRequestRevisionAnnotationKey: "1338",
},
},
Spec: cmapi.CertificateRequestSpec{
Usages: []cmapi.KeyUsage{cmapi.UsageServerAuth, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment},
Duration: &metav1.Duration{Duration: DefaultCertificateDuration},
},
},
wantCSR: &x509.CertificateRequest{
SignatureAlgorithm: x509.SHA512WithRSA,
PublicKeyAlgorithm: x509.RSA,
Subject: pkix.Name{
CommonName: "",
},
DNSNames: []string{"some-host.some-domain.tld"},
IPAddresses: []net.IP(nil),
URIs: []*url.URL(nil),
},
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 6762f17

Please sign in to comment.