Skip to content

Commit 0f617a3

Browse files
authored
Merge pull request kubernetes#76500 from rojkov/issue-1399
kubeadm: check all available CA certs against pinned certs
2 parents 888b81b + 7f8fc5d commit 0f617a3

File tree

4 files changed

+44
-24
lines changed

4 files changed

+44
-24
lines changed

cmd/kubeadm/app/discovery/token/token.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,14 @@ func RetrieveValidatedConfigInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmda
119119
for _, cluster := range insecureConfig.Clusters {
120120
clusterCABytes = cluster.CertificateAuthorityData
121121
}
122-
clusterCA, err := parsePEMCert(clusterCABytes)
122+
clusterCAs, err := parsePEMCerts(clusterCABytes)
123123
if err != nil {
124124
return nil, errors.Wrapf(err, "failed to parse cluster CA from the %s configmap", bootstrapapi.ConfigMapClusterInfo)
125125

126126
}
127127

128128
// Validate the cluster CA public key against the pinned set
129-
err = pubKeyPins.Check(clusterCA)
129+
err = pubKeyPins.CheckAny(clusterCAs)
130130
if err != nil {
131131
return nil, errors.Wrapf(err, "cluster CA found in %s configmap is invalid", bootstrapapi.ConfigMapClusterInfo)
132132
}
@@ -226,14 +226,27 @@ func fetchKubeConfigWithTimeout(apiEndpoint string, discoveryTimeout time.Durati
226226
}
227227
}
228228

229-
// parsePEMCert decodes a PEM-formatted certificate and returns it as an x509.Certificate
230-
func parsePEMCert(certData []byte) (*x509.Certificate, error) {
231-
pemBlock, trailingData := pem.Decode(certData)
232-
if pemBlock == nil {
233-
return nil, errors.New("invalid PEM data")
234-
}
235-
if len(trailingData) != 0 {
236-
return nil, errors.New("trailing data after first PEM block")
229+
// parsePEMCerts decodes PEM-formatted certificates into a slice of x509.Certificates
230+
func parsePEMCerts(certData []byte) ([]*x509.Certificate, error) {
231+
var certificates []*x509.Certificate
232+
var pemBlock *pem.Block
233+
234+
for {
235+
pemBlock, certData = pem.Decode(certData)
236+
if pemBlock == nil {
237+
return nil, errors.New("invalid PEM data")
238+
}
239+
240+
cert, err := x509.ParseCertificate(pemBlock.Bytes)
241+
if err != nil {
242+
return nil, errors.Wrap(err, "unable to parse certificate")
243+
}
244+
certificates = append(certificates, cert)
245+
246+
if len(certData) == 0 {
247+
break
248+
}
237249
}
238-
return x509.ParseCertificate(pemBlock.Bytes)
250+
251+
return certificates, nil
239252
}

cmd/kubeadm/app/discovery/token/token_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,24 @@ func TestParsePEMCert(t *testing.T) {
103103
}{
104104
{"invalid certificate data", []byte{0}, false},
105105
{"certificate with junk appended", []byte(testCertPEM + "\nABC"), false},
106-
{"multiple certificates", []byte(testCertPEM + "\n" + testCertPEM), false},
106+
{"multiple certificates", []byte(testCertPEM + "\n" + testCertPEM), true},
107107
{"valid", []byte(testCertPEM), true},
108+
{"empty input", []byte{}, false},
108109
} {
109-
cert, err := parsePEMCert(testCase.input)
110+
certs, err := parsePEMCerts(testCase.input)
110111
if testCase.expectValid {
111112
if err != nil {
112113
t.Errorf("failed TestParsePEMCert(%s): unexpected error %v", testCase.name, err)
113114
}
114-
if cert == nil {
115+
if certs == nil {
115116
t.Errorf("failed TestParsePEMCert(%s): returned nil", testCase.name)
116117
}
117118
} else {
118119
if err == nil {
119120
t.Errorf("failed TestParsePEMCert(%s): expected an error", testCase.name)
120121
}
121-
if cert != nil {
122-
t.Errorf("failed TestParsePEMCert(%s): expected not to get a certificate back, but got one", testCase.name)
122+
if certs != nil {
123+
t.Errorf("failed TestParsePEMCert(%s): expected not to get a certificate back, but got some", testCase.name)
123124
}
124125
}
125126
}

cmd/kubeadm/app/util/pubkeypin/pubkeypin.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,18 @@ func (s *Set) Allow(pubKeyHashes ...string) error {
6161
return nil
6262
}
6363

64-
// Check if a certificate matches one of the public keys in the set
65-
func (s *Set) Check(certificate *x509.Certificate) error {
66-
if s.checkSHA256(certificate) {
67-
return nil
64+
// CheckAny checks if at least one certificate matches one of the public keys in the set
65+
func (s *Set) CheckAny(certificates []*x509.Certificate) error {
66+
var hashes []string
67+
68+
for _, certificate := range certificates {
69+
if s.checkSHA256(certificate) {
70+
return nil
71+
}
72+
73+
hashes = append(hashes, Hash(certificate))
6874
}
69-
return errors.Errorf("public key %s not pinned", Hash(certificate))
75+
return errors.Errorf("none of the public keys %q are pinned", strings.Join(hashes, ":"))
7076
}
7177

7278
// Empty returns true if the Set contains no pinned public keys.

cmd/kubeadm/app/util/pubkeypin/pubkeypin_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func TestSet(t *testing.T) {
121121
return
122122
}
123123

124-
err = s.Check(testCert(t, testCertPEM))
124+
err = s.CheckAny([]*x509.Certificate{testCert(t, testCertPEM)})
125125
if err == nil {
126126
t.Error("expected test cert to not be allowed (yet)")
127127
return
@@ -133,13 +133,13 @@ func TestSet(t *testing.T) {
133133
return
134134
}
135135

136-
err = s.Check(testCert(t, testCertPEM))
136+
err = s.CheckAny([]*x509.Certificate{testCert(t, testCertPEM)})
137137
if err != nil {
138138
t.Errorf("expected test cert to be allowed, but got back: %v", err)
139139
return
140140
}
141141

142-
err = s.Check(testCert(t, testCert2PEM))
142+
err = s.CheckAny([]*x509.Certificate{testCert(t, testCert2PEM)})
143143
if err == nil {
144144
t.Error("expected the second test cert to be disallowed")
145145
return

0 commit comments

Comments
 (0)