Skip to content

Commit 44f5a75

Browse files
authored
Merge pull request kubernetes#85480 from tnozicka/apiserver-better-error
Add certificate identification to error message when x509 auth fails
2 parents c6f7fbc + bf52770 commit 44f5a75

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

staging/src/k8s.io/apiserver/pkg/authentication/request/x509/x509.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ package x509
1919
import (
2020
"crypto/x509"
2121
"crypto/x509/pkix"
22+
"encoding/hex"
2223
"fmt"
2324
"net/http"
25+
"strings"
2426
"time"
2527

2628
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -82,6 +84,27 @@ func (f UserConversionFunc) User(chain []*x509.Certificate) (*authenticator.Resp
8284
return f(chain)
8385
}
8486

87+
func columnSeparatedHex(d []byte) string {
88+
h := strings.ToUpper(hex.EncodeToString(d))
89+
var sb strings.Builder
90+
for i, r := range h {
91+
sb.WriteRune(r)
92+
if i%2 == 1 && i != len(h)-1 {
93+
sb.WriteRune(':')
94+
}
95+
}
96+
return sb.String()
97+
}
98+
99+
func certificateIdentifier(c *x509.Certificate) string {
100+
return fmt.Sprintf(
101+
"SN=%d, SKID=%s, AKID=%s",
102+
c.SerialNumber,
103+
columnSeparatedHex(c.SubjectKeyId),
104+
columnSeparatedHex(c.AuthorityKeyId),
105+
)
106+
}
107+
85108
// VerifyOptionFunc is function which provides a shallow copy of the VerifyOptions to the authenticator. This allows
86109
// for cases where the options (particularly the CAs) can change. If the bool is false, then the returned VerifyOptions
87110
// 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
129152
clientCertificateExpirationHistogram.Observe(remaining.Seconds())
130153
chains, err := req.TLS.PeerCertificates[0].Verify(optsCopy)
131154
if err != nil {
132-
return nil, false, err
155+
return nil, false, fmt.Errorf(
156+
"verifying certificate %s failed: %w",
157+
certificateIdentifier(req.TLS.PeerCertificates[0]),
158+
err,
159+
)
133160
}
134161

135162
var errlist []error

staging/src/k8s.io/apiserver/pkg/authentication/request/x509/x509_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,8 @@ func getCertsFromFile(t *testing.T, names ...string) []*x509.Certificate {
881881
}
882882

883883
func getCert(t *testing.T, pemData string) *x509.Certificate {
884+
t.Helper()
885+
884886
pemBlock, _ := pem.Decode([]byte(pemData))
885887
cert, err := x509.ParseCertificate(pemBlock.Bytes)
886888
if err != nil {
@@ -897,3 +899,58 @@ func getCerts(t *testing.T, pemData ...string) []*x509.Certificate {
897899
}
898900
return certs
899901
}
902+
903+
func TestCertificateIdentifier(t *testing.T) {
904+
tt := []struct {
905+
name string
906+
cert *x509.Certificate
907+
expectedIdentifier string
908+
}{
909+
{
910+
name: "client cert",
911+
cert: getCert(t, clientCNCert),
912+
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",
913+
},
914+
{
915+
name: "nil serial",
916+
cert: func() *x509.Certificate {
917+
c := getCert(t, clientCNCert)
918+
c.SerialNumber = nil
919+
return c
920+
}(),
921+
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",
922+
},
923+
{
924+
name: "empty SKID",
925+
cert: func() *x509.Certificate {
926+
c := getCert(t, clientCNCert)
927+
c.SubjectKeyId = nil
928+
return c
929+
}(),
930+
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",
931+
},
932+
{
933+
name: "empty AKID",
934+
cert: func() *x509.Certificate {
935+
c := getCert(t, clientCNCert)
936+
c.AuthorityKeyId = nil
937+
return c
938+
}(),
939+
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=",
940+
},
941+
{
942+
name: "self-signed",
943+
cert: getCert(t, selfSignedCert),
944+
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",
945+
},
946+
}
947+
948+
for _, tc := range tt {
949+
t.Run(tc.name, func(t *testing.T) {
950+
got := certificateIdentifier(tc.cert)
951+
if got != tc.expectedIdentifier {
952+
t.Errorf("expected %q, got %q", tc.expectedIdentifier, got)
953+
}
954+
})
955+
}
956+
}

0 commit comments

Comments
 (0)