Merge pull request #85480 from tnozicka/apiserver-better-error

Add certificate identification to error message when x509 auth fails
This commit is contained in:
Kubernetes Prow Robot 2020-10-23 00:11:44 -07:00 committed by GitHub
commit 44f5a75e6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 1 deletions

View File

@ -19,8 +19,10 @@ package x509
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"fmt"
"net/http"
"strings"
"time"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@ -82,6 +84,27 @@ func (f UserConversionFunc) User(chain []*x509.Certificate) (*authenticator.Resp
return f(chain)
}
func columnSeparatedHex(d []byte) string {
h := strings.ToUpper(hex.EncodeToString(d))
var sb strings.Builder
for i, r := range h {
sb.WriteRune(r)
if i%2 == 1 && i != len(h)-1 {
sb.WriteRune(':')
}
}
return sb.String()
}
func certificateIdentifier(c *x509.Certificate) string {
return fmt.Sprintf(
"SN=%d, SKID=%s, AKID=%s",
c.SerialNumber,
columnSeparatedHex(c.SubjectKeyId),
columnSeparatedHex(c.AuthorityKeyId),
)
}
// VerifyOptionFunc is function which provides a shallow copy of the VerifyOptions to the authenticator. This allows
// for cases where the options (particularly the CAs) can change. If the bool is false, then the returned VerifyOptions
// are ignored and the authenticator will express "no opinion". This allows a clear signal for cases where a CertPool
@ -129,7 +152,11 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.R
clientCertificateExpirationHistogram.Observe(remaining.Seconds())
chains, err := req.TLS.PeerCertificates[0].Verify(optsCopy)
if err != nil {
return nil, false, err
return nil, false, fmt.Errorf(
"verifying certificate %s failed: %w",
certificateIdentifier(req.TLS.PeerCertificates[0]),
err,
)
}
var errlist []error

View File

@ -881,6 +881,8 @@ func getCertsFromFile(t *testing.T, names ...string) []*x509.Certificate {
}
func getCert(t *testing.T, pemData string) *x509.Certificate {
t.Helper()
pemBlock, _ := pem.Decode([]byte(pemData))
cert, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
@ -897,3 +899,58 @@ func getCerts(t *testing.T, pemData ...string) []*x509.Certificate {
}
return certs
}
func TestCertificateIdentifier(t *testing.T) {
tt := []struct {
name string
cert *x509.Certificate
expectedIdentifier string
}{
{
name: "client cert",
cert: getCert(t, clientCNCert),
expectedIdentifier: "SN=1, SKID=E7:FB:1F:45:F0:71:77:AF:8C:10:4A:0A:42:03:F5:1F:1F:07:CF:DF, AKID=3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78",
},
{
name: "nil serial",
cert: func() *x509.Certificate {
c := getCert(t, clientCNCert)
c.SerialNumber = nil
return c
}(),
expectedIdentifier: "SN=<nil>, SKID=E7:FB:1F:45:F0:71:77:AF:8C:10:4A:0A:42:03:F5:1F:1F:07:CF:DF, AKID=3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78",
},
{
name: "empty SKID",
cert: func() *x509.Certificate {
c := getCert(t, clientCNCert)
c.SubjectKeyId = nil
return c
}(),
expectedIdentifier: "SN=1, SKID=, AKID=3D:F0:F7:30:3D:3B:EB:3A:55:68:FA:F5:43:C9:C7:AC:E1:3F:10:78",
},
{
name: "empty AKID",
cert: func() *x509.Certificate {
c := getCert(t, clientCNCert)
c.AuthorityKeyId = nil
return c
}(),
expectedIdentifier: "SN=1, SKID=E7:FB:1F:45:F0:71:77:AF:8C:10:4A:0A:42:03:F5:1F:1F:07:CF:DF, AKID=",
},
{
name: "self-signed",
cert: getCert(t, selfSignedCert),
expectedIdentifier: "SN=14307769263086146430, SKID=7C:AB:02:A8:45:3F:B0:28:2F:71:91:52:A2:71:EE:D9:40:2B:43:71, AKID=7C:AB:02:A8:45:3F:B0:28:2F:71:91:52:A2:71:EE:D9:40:2B:43:71",
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
got := certificateIdentifier(tc.cert)
if got != tc.expectedIdentifier {
t.Errorf("expected %q, got %q", tc.expectedIdentifier, got)
}
})
}
}