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

Add certificate identification to error message when x509 auth fails #85480

Merged
merged 1 commit into from Oct 23, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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 {
Expand All @@ -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)
}
})
}
}