Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement private key size annotation #48

Merged
merged 1 commit into from
Dec 14, 2023
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
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