Skip to content

Commit

Permalink
control-plane-pki-operator: validate CN for CSR
Browse files Browse the repository at this point in the history
We were being too permissive for the CN provided in the CSRs we signed.
Now, they must have a prefix identifying them as coming from the signer
they're using, which makes the requests sent by these credentials
auditable.

Signed-off-by: Steve Kuznetsov <skuznets@redhat.com>
  • Loading branch information
stevekuznetsov authored and openshift-cherrypick-robot committed Feb 9, 2024
1 parent a847978 commit 09e07bd
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 67 deletions.
16 changes: 16 additions & 0 deletions control-plane-pki-operator/certificates/validation.go
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"strings"

hypershiftv1beta1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
"github.com/openshift/hypershift/hypershift-operator/controllers/manifests"
Expand Down Expand Up @@ -52,6 +53,10 @@ func SignerNameForHC(hc *hypershiftv1beta1.HostedCluster, signer SignerClass) st
// SignerDomain is the domain all certificate signers identify under for HyperShift
const SignerDomain string = "hypershift.openshift.io"

func CommonNamePrefix(signer SignerClass) string {
return fmt.Sprintf("system:%s:", signer)
}

// ValidatorFunc knows how to validate a CertificateSigningRequest
type ValidatorFunc func(csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest) error

Expand All @@ -61,10 +66,21 @@ func Validator(hcp *hypershiftv1beta1.HostedControlPlane, signer SignerClass) Va
requiredUsages, optionalUsages := ValidUsagesFor(signer)
validUsages := optionalUsages.Union(requiredUsages)
return func(csr *certificatesv1.CertificateSigningRequest, x509cr *x509.CertificateRequest) error {
if csr == nil {
return errors.New("the Kubernetes CertificateSigningRequest object is missing - programmer error")
}
if x509cr == nil {
return errors.New("the x509 CertificateRequest object is missing - programmer error")
}

if csr.Spec.SignerName != signerName {
return fmt.Errorf("signer name %q does not match %q", csr.Spec.SignerName, signerName)
}

if prefix := CommonNamePrefix(signer); !strings.HasPrefix(x509cr.Subject.CommonName, prefix) {
return fmt.Errorf("invalid certificate request: subject CommonName must begin with %q", prefix)
}

requestedUsages := sets.New[certificatesv1.KeyUsage](csr.Spec.Usages...)
if !requestedUsages.IsSuperset(requiredUsages) {
return fmt.Errorf("missing required usages: %v", requiredUsages.Difference(requestedUsages))
Expand Down
40 changes: 37 additions & 3 deletions control-plane-pki-operator/certificates/validation_test.go
Expand Up @@ -2,6 +2,7 @@ package certificates

import (
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"net"
"net/url"
Expand All @@ -27,6 +28,9 @@ func TestValidator(t *testing.T) {
SignerName: "invalid",
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
expectedErr: true,
},
{
Expand All @@ -36,6 +40,9 @@ func TestValidator(t *testing.T) {
SignerName: "hypershift.openshift.io/other",
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
expectedErr: true,
},
{
Expand All @@ -46,6 +53,9 @@ func TestValidator(t *testing.T) {
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageDigitalSignature},
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
expectedErr: true,
},
{
Expand All @@ -56,6 +66,9 @@ func TestValidator(t *testing.T) {
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageEmailProtection},
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
expectedErr: true,
},
{
Expand All @@ -67,6 +80,7 @@ func TestValidator(t *testing.T) {
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
DNSNames: []string{"example.com"},
},
expectedErr: true,
Expand All @@ -80,6 +94,7 @@ func TestValidator(t *testing.T) {
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
EmailAddresses: []string{"someone@example.com"},
},
expectedErr: true,
Expand All @@ -93,6 +108,7 @@ func TestValidator(t *testing.T) {
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
IPAddresses: []net.IP{[]byte(`127.0.0.1`)},
},
expectedErr: true,
Expand All @@ -106,7 +122,21 @@ func TestValidator(t *testing.T) {
},
},
x509cr: &x509.CertificateRequest{
URIs: []*url.URL{{Scheme: "https"}},
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
URIs: []*url.URL{{Scheme: "https"}},
},
expectedErr: true,
},
{
name: "invalid request: common name without correct prefix",
csr: &certificatesv1.CertificateSigningRequest{
Spec: certificatesv1.CertificateSigningRequestSpec{
SignerName: "hypershift.openshift.io/hc-namespace-hc-name.customer-break-glass",
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
},
},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: "something"},
},
expectedErr: true,
},
Expand All @@ -118,7 +148,9 @@ func TestValidator(t *testing.T) {
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
},
},
x509cr: &x509.CertificateRequest{},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
},
{
name: "valid: client auth with extras",
Expand All @@ -128,7 +160,9 @@ func TestValidator(t *testing.T) {
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth, certificatesv1.UsageDigitalSignature, certificatesv1.UsageKeyEncipherment},
},
},
x509cr: &x509.CertificateRequest{},
x509cr: &x509.CertificateRequest{
Subject: pkix.Name{CommonName: CommonNamePrefix(CustomerBreakGlassSigner) + "user"},
},
},
},
} {
Expand Down
Expand Up @@ -247,7 +247,7 @@ func TestCertificateSigningController_processCertificateSigningRequest(t *testin
Name: name,
},
Spec: testCSRSpec(csrBuilder{
cn: "test-client",
cn: certificates.CommonNamePrefix(certificates.CustomerBreakGlassSigner) + "test-client",
org: []string{"anything"},
signerName: "hypershift.openshift.io/hc-namespace-hc-name.customer-break-glass",
usages: []certificatesv1.KeyUsage{certificatesv1.UsageContentCommitment},
Expand Down Expand Up @@ -290,7 +290,7 @@ func TestCertificateSigningController_processCertificateSigningRequest(t *testin
Name: name,
},
Spec: testCSRSpec(csrBuilder{
cn: "test-client",
cn: certificates.CommonNamePrefix(certificates.CustomerBreakGlassSigner) + "test-client",
org: []string{"system:masters"},
signerName: "hypershift.openshift.io/hc-namespace-hc-name.customer-break-glass",
usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth},
Expand Down
Expand Up @@ -9,6 +9,7 @@ import (

hypershiftv1beta1 "github.com/openshift/hypershift/api/hypershift/v1beta1"
hypershiftv1beta1client "github.com/openshift/hypershift/client/clientset/clientset/typed/hypershift/v1beta1"
"github.com/openshift/hypershift/control-plane-pki-operator/certificates"
"github.com/openshift/hypershift/control-plane-pki-operator/clienthelpers"
pkimanifests "github.com/openshift/hypershift/control-plane-pki-operator/manifests"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -72,7 +73,6 @@ func NewCertRotationController(
if err != nil {
return nil, err
}
userName := "customer-break-glass-" + userNameSuffix
uid, err := randomString(128)
if err != nil {
return nil, err
Expand Down Expand Up @@ -111,7 +111,11 @@ func NewCertRotationController(
Validity: 36 * rotationDay / 24,
Refresh: 6 * rotationDay / 24,
CertCreator: &certrotation.ClientRotation{
UserInfo: &user.DefaultInfo{Name: userName, UID: uid, Groups: []string{"system:masters"}},
UserInfo: &user.DefaultInfo{
Name: certificates.CommonNamePrefix(certificates.CustomerBreakGlassSigner) + userNameSuffix,
UID: uid,
Groups: []string{"system:masters"},
},
},
Informer: kubeInformersForNamespaces.InformersFor(hostedControlPlane.Namespace).Core().V1().Secrets(),
Lister: kubeInformersForNamespaces.InformersFor(hostedControlPlane.Namespace).Core().V1().Secrets().Lister(),
Expand Down
13 changes: 9 additions & 4 deletions test/integration/control_plane_pki_operator.go
Expand Up @@ -48,7 +48,7 @@ func RunTestControlPlanePKIOperatorBreakGlassCredentials(t *testing.T, ctx conte
return breakGlassTenantClient
}

validateCertificateAuth := func(t *testing.T, root *rest.Config, crt, key []byte) {
validateCertificateAuth := func(t *testing.T, root *rest.Config, crt, key []byte, usernameValid func(string) bool) {
t.Log("validating that the client certificate provides the appropriate access")

breakGlassTenantClient := clientForCertKey(t, root, crt, key)
Expand All @@ -60,7 +60,8 @@ func RunTestControlPlanePKIOperatorBreakGlassCredentials(t *testing.T, ctx conte
}

t.Log("ensuring that the SSR identifies the client certificate as having system:masters power and correct username")
if !sets.New[string](response.Status.UserInfo.Groups...).Has("system:masters") || !strings.HasPrefix(response.Status.UserInfo.Username, "customer-break-glass-") {
if !sets.New[string](response.Status.UserInfo.Groups...).Has("system:masters") ||
!usernameValid(response.Status.UserInfo.Username) {
t.Fatalf("did not get correct SSR response: %#v", response)
}
}
Expand All @@ -78,7 +79,9 @@ func RunTestControlPlanePKIOperatorBreakGlassCredentials(t *testing.T, ctx conte
t.Fatalf("client cert didn't become available: %v", err)
}

validateCertificateAuth(t, guest.Cfg, clientCertificate.Data["tls.crt"], clientCertificate.Data["tls.key"])
validateCertificateAuth(t, guest.Cfg, clientCertificate.Data["tls.crt"], clientCertificate.Data["tls.key"], func(s string) bool {
return strings.HasPrefix(s, certificates.CommonNamePrefix(certificates.CustomerBreakGlassSigner))
})
})

t.Run("CSR flow", func(t *testing.T) {
Expand Down Expand Up @@ -143,7 +146,9 @@ func RunTestControlPlanePKIOperatorBreakGlassCredentials(t *testing.T, ctx conte
t.Fatal("got a zero-length signed cert back")
}

validateCertificateAuth(t, guest.Cfg, signedCrt, key)
validateCertificateAuth(t, guest.Cfg, signedCrt, key, func(s string) bool {
return s == framework.CommonName()
})

t.Run("revocation", func(t *testing.T) {
crrName := "customer-break-glass-revocation"
Expand Down
7 changes: 6 additions & 1 deletion test/integration/framework/pki.go
Expand Up @@ -11,6 +11,7 @@ import (
"testing"
"time"

"github.com/openshift/hypershift/control-plane-pki-operator/certificates"
librarygocrypto "github.com/openshift/library-go/pkg/crypto"
)

Expand Down Expand Up @@ -42,7 +43,7 @@ func CertKeyRequest(t *testing.T) ([]byte, []byte, []byte) {

csr, err := x509.CreateCertificateRequest(rand.New(rand.NewSource(0)), &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "customer-break-glass-test-whatever",
CommonName: CommonName(),
Organization: []string{"system:masters"},
},
}, cfg.Key)
Expand Down Expand Up @@ -74,3 +75,7 @@ func CertKeyRequest(t *testing.T) ([]byte, []byte, []byte) {
}
return crtb, keyb, csrb
}

func CommonName() string {
return certificates.CommonNamePrefix(certificates.CustomerBreakGlassSigner) + "e2e-test"
}
28 changes: 14 additions & 14 deletions test/integration/framework/testdata/csr.pem
@@ -1,16 +1,16 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICizCCAXMCAQAwRjEXMBUGA1UEChMOc3lzdGVtOm1hc3RlcnMxKzApBgNVBAMT
ImN1c3RvbWVyLWJyZWFrLWdsYXNzLXRlc3Qtd2hhdGV2ZXIwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDKlG9uq7U+ppVgv4kpVDtq5E/+xfvbdb543gVR
IXjd3N6jQ5jtqI8Cj/Nb6+HTYRWwufIMUASQAc2wbZlkq5RtgylF6CwgJzFNDld7
23U3AxUukpaXclAHqLglXqR6xpf7FnFZQHeSBnHKmYg0Lf3kMDjkhZoZBH3PrdN1
YE4xnzN1rXaFP0Fa4dnibCOBiZwHu0h/zxosEp23P+kHfDPLYvOfWtaVLbNI5LXQ
7Tfft2lzM3laR852R2CvOSMUNEpcQxQXh9MgXr0deFoUyBOVC9OynGX1V0z0j8HP
XDYRtoruYFkDvEzm0IupmQkHxH4eyv8YoXgT1yWWHdJ8goB3AgMBAAGgADANBgkq
hkiG9w0BAQsFAAOCAQEABJHn7bg3M8h1W0Iu9/Bdd6cg+L0oCYNAgICPPChpIzO3
nbACcjVBbJVq9yy/vcJbB+Gh7AQMYqisDIyPTqS/cB9qn+9eC9bqIM/KZCXuwekb
1RTV+NT7nIzHSKo+qG+fE3M6ni4n2VHYDtfqBIb2Yz3jIsYfMqxjT64n/JAetGzY
E7DaenJzXUfER4OHZRQsoi6qtMW51SbX5b+jqoMvcD6bcaZMeILY+GHa8RnqoAEM
RzhrUR0htcsbGPNgj7MCdJ656AsDPtbzVj+ehsVyJj9ICMNKlR/NeNV28h2WhOfK
vIMD9MA10y1a2RhUhHeeDj2ymSolA8OOhqTaLcYDvw==
MIICjTCCAXUCAQAwSDEXMBUGA1UEChMOc3lzdGVtOm1hc3RlcnMxLTArBgNVBAMT
JHN5c3RlbTpjdXN0b21lci1icmVhay1nbGFzczplMmUtdGVzdDCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANG938Bt0pxZsEhf04zr1bm31/QrmZdDUdh3
YPpHhLfwUu7QZxHaiUPmrjTnQld35CzNQD/U3/3D2ZmYYiQmenhi5mQGQ7e2cB+V
EouQ3nSuZRWbKr6VAnxzo2YFf+s2mrtOpo7uxkDJzJh+pL+bhdqfF0K66hNZCDTu
Re4vdT/vTh6GEal/SUqhlgGQwBML9jioMUHPFwvZOhBS8RE7pgWCeT13t1JcqqWx
m+InstePSwZTDYlBWYhcDk+cmZOoqjvhy7GPyTqn2B8XFMntwcfga0Tj64/ipHH0
/dEcYFXosKpg7bmRokqS8cQRxP2bB+WT7l0WyottZY9EhtJP60MCAwEAAaAAMA0G
CSqGSIb3DQEBCwUAA4IBAQBmmNlkYfEhLlDOvZ0tK3f8zsBt4Iw+siwt9q/eNunB
l78ShwRn0k3MP1NPZwB1Rg9hyMiM46u2YJ+oqk4DBtNocdYSbUEtAugxVdsCyhOt
iCNVYdgfos58F5AOV+KlMbDx+I/9O1p7DN8sS60UpBQMdx5hRQ5E+1ed4oWV4wwU
4A8iexS7IKOC1ZsKoQEElKLU6pfVAqjjPwhB+SdNYquk/vBiTVgsJ+wODh3sQvfj
fduMwWz9HeLnP03Z/zTWsoM1kn6e4eM3sLBS+uJ8sht+5ZUPlCohsN7yzv9a/rz/
EcVj0OkW7srdp9ksI37PJT/g60FVrzsr2IXGEmcelBLC
-----END CERTIFICATE REQUEST-----
32 changes: 16 additions & 16 deletions test/integration/framework/testdata/tls.crt
@@ -1,19 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIIJYRaVt0jLygwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE
AxMLdGVzdC1zaWduZXIwIBcNMjQwMTEyMTg0MjQ5WhgPMjEyMzEyMTkxODQyNTBa
MIIDEzCCAfugAwIBAgIIB5b+weFybZAwDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UE
AxMLdGVzdC1zaWduZXIwIBcNMjQwMjA2MTYzMDQ2WhgPMjEyNDAxMTMxNjMwNDda
MBYxFDASBgNVBAMTC3Rlc3Qtc2lnbmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAypRvbqu1PqaVYL+JKVQ7auRP/sX723W+eN4FUSF43dzeo0OY7aiP
Ao/zW+vh02EVsLnyDFAEkAHNsG2ZZKuUbYMpRegsICcxTQ5Xe9t1NwMVLpKWl3JQ
B6i4JV6kesaX+xZxWUB3kgZxypmINC395DA45IWaGQR9z63TdWBOMZ8zda12hT9B
WuHZ4mwjgYmcB7tIf88aLBKdtz/pB3wzy2Lzn1rWlS2zSOS10O0337dpczN5WkfO
dkdgrzkjFDRKXEMUF4fTIF69HXhaFMgTlQvTspxl9VdM9I/Bz1w2EbaK7mBZA7xM
5tCLqZkJB8R+Hsr/GKF4E9cllh3SfIKAdwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMC
AqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUYyv0lfCRrAiW3QS3L40SJ0rA
Gw0wHwYDVR0jBBgwFoAUYyv0lfCRrAiW3QS3L40SJ0rAGw0wDQYJKoZIhvcNAQEL
BQADggEBAE0aH7vUXliM+0DK2FJ6+L0wosDCWeNwyl6So0TmUp7Rg6jWdeRiPFUS
BSwdAX8LXrSAFSetPO3+Z2wPmUK7YZEDfM0TSvFr3ZU/qhPTxQJRvF4vFFJr8T4I
TnXuwfAYsWr7kmCP9PyMF7wljW4sG7PbmYcsE0ZeV71HIdSxfjbVgC9jilau5Lay
HBHzQqE9oTUSfprvE8o/NnIHAToN/dh9qSf9skr6NhjE95FlOBk4WEbm+G0nJ24G
X144E8NnPFK/Ajw7/UE4xUqsTq2jTR4YbmOZ5UlNkCdH0O7b083HjlyLomelae+Z
EX1XAvMly6kziE0K5J3YE6NXBH0FkSs=
MIIBCgKCAQEA0b3fwG3SnFmwSF/TjOvVubfX9CuZl0NR2Hdg+keEt/BS7tBnEdqJ
Q+auNOdCV3fkLM1AP9Tf/cPZmZhiJCZ6eGLmZAZDt7ZwH5USi5DedK5lFZsqvpUC
fHOjZgV/6zaau06mju7GQMnMmH6kv5uF2p8XQrrqE1kINO5F7i91P+9OHoYRqX9J
SqGWAZDAEwv2OKgxQc8XC9k6EFLxETumBYJ5PXe3UlyqpbGb4iey149LBlMNiUFZ
iFwOT5yZk6iqO+HLsY/JOqfYHxcUye3Bx+BrROPrj+KkcfT90RxgVeiwqmDtuZGi
SpLxxBHE/ZsH5ZPuXRbKi21lj0SG0k/rQwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMC
AqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUZ6ebFTmM9zi81j7oX2UkJvwV
tWUwHwYDVR0jBBgwFoAUZ6ebFTmM9zi81j7oX2UkJvwVtWUwDQYJKoZIhvcNAQEL
BQADggEBAG+P5r2I2ZSo/68cS3QRsHEBJsSVLHz+5bSM8wQheCLsc+fSSVFUDu1k
KDySJ37LI1E4IoqRRhO68FJvV310WrJoqcP2WyerZTEu3O6sTflgrL5nfoQYyDOf
0iQh/dMEMSuEdqOH63530hGsXlzK+nDvGV96TJH1fgFVdACKpQY+CUc3F2ppxr1F
mMy2mqC9VTBPeeDlceY980qqCFd88RgQOpkUNnP1AeOnkP7fno1TfNQWJIIsjy2u
zQwzNqQC0Y9hN+dw54qUDn3O4Eo2i+B8Gcod4IZ0F1ttaadOOuZwbhNp77iBMKc+
4c5DBhJo2bMIq/cvvtZ+PjoXUA+3AN8=
-----END CERTIFICATE-----
50 changes: 25 additions & 25 deletions test/integration/framework/testdata/tls.key
@@ -1,27 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

0 comments on commit 09e07bd

Please sign in to comment.