Skip to content

Commit bbd83d8

Browse files
authored
Merge pull request kubernetes#125634 from ahmedtd/x509credentialID
Define credential IDs for X.509 certificates
2 parents f164b47 + 2ad2bd8 commit bbd83d8

File tree

5 files changed

+93
-58
lines changed

5 files changed

+93
-58
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package x509
1818

1919
import (
20+
"crypto/sha256"
2021
"crypto/x509"
2122
"crypto/x509/pkix"
2223
"encoding/hex"
@@ -276,10 +277,17 @@ var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate
276277
if len(chain[0].Subject.CommonName) == 0 {
277278
return nil, false, nil
278279
}
280+
281+
fp := sha256.Sum256(chain[0].Raw)
282+
id := "X509SHA256=" + hex.EncodeToString(fp[:])
283+
279284
return &authenticator.Response{
280285
User: &user.DefaultInfo{
281286
Name: chain[0].Subject.CommonName,
282287
Groups: chain[0].Subject.Organization,
288+
Extra: map[string][]string{
289+
user.CredentialIDKey: {id},
290+
},
283291
},
284292
}, true, nil
285293
})

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

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323
"errors"
2424
"io/ioutil"
2525
"net/http"
26-
"reflect"
2726
"sort"
2827
"testing"
2928
"time"
3029

30+
"github.com/google/go-cmp/cmp"
3131
"k8s.io/apimachinery/pkg/util/sets"
3232
"k8s.io/apiserver/pkg/authentication/authenticator"
3333
"k8s.io/apiserver/pkg/authentication/user"
@@ -581,9 +581,8 @@ func TestX509(t *testing.T) {
581581
Opts x509.VerifyOptions
582582
User UserConversion
583583

584-
ExpectUserName string
585-
ExpectGroups []string
586584
ExpectOK bool
585+
ExpectResponse *authenticator.Response
587586
ExpectErr bool
588587
}{
589588
"non-tls": {
@@ -618,21 +617,35 @@ func TestX509(t *testing.T) {
618617
Certs: getCerts(t, serverCert),
619618
User: CommonNameUserConversion,
620619

621-
ExpectUserName: "127.0.0.1",
622-
ExpectGroups: []string{"My Org"},
623-
ExpectOK: true,
624-
ExpectErr: false,
620+
ExpectOK: true,
621+
ExpectResponse: &authenticator.Response{
622+
User: &user.DefaultInfo{
623+
Name: "127.0.0.1",
624+
Groups: []string{"My Org"},
625+
Extra: map[string][]string{
626+
user.CredentialIDKey: {"X509SHA256=92209d1e0dd36a018f244f5e1b88e2d47b049e9cfcd4b7c87c65875866872230"},
627+
},
628+
},
629+
},
630+
ExpectErr: false,
625631
},
626632

627633
"common name": {
628634
Opts: getDefaultVerifyOptions(t),
629635
Certs: getCerts(t, clientCNCert),
630636
User: CommonNameUserConversion,
631637

632-
ExpectUserName: "client_cn",
633-
ExpectGroups: []string{"My Org"},
634-
ExpectOK: true,
635-
ExpectErr: false,
638+
ExpectOK: true,
639+
ExpectResponse: &authenticator.Response{
640+
User: &user.DefaultInfo{
641+
Name: "client_cn",
642+
Groups: []string{"My Org"},
643+
Extra: map[string][]string{
644+
user.CredentialIDKey: {"X509SHA256=dd0a6a295055fa94455c522b0d54ef0499186f454a7cf978b8b346dc35b254f7"},
645+
},
646+
},
647+
},
648+
ExpectErr: false,
636649
},
637650
"ca with multiple organizations": {
638651
Opts: x509.VerifyOptions{
@@ -641,10 +654,17 @@ func TestX509(t *testing.T) {
641654
Certs: getCerts(t, caWithGroups),
642655
User: CommonNameUserConversion,
643656

644-
ExpectUserName: "ROOT CA WITH GROUPS",
645-
ExpectGroups: []string{"My Org", "My Org 1", "My Org 2"},
646-
ExpectOK: true,
647-
ExpectErr: false,
657+
ExpectOK: true,
658+
ExpectResponse: &authenticator.Response{
659+
User: &user.DefaultInfo{
660+
Name: "ROOT CA WITH GROUPS",
661+
Groups: []string{"My Org", "My Org 1", "My Org 2"},
662+
Extra: map[string][]string{
663+
user.CredentialIDKey: {"X509SHA256=6f337bb6576b6f942bd5ac5256f621e352aa7b34d971bda9b8f8981f51bba456"},
664+
},
665+
},
666+
},
667+
ExpectErr: false,
648668
},
649669

650670
"custom conversion error": {
@@ -664,9 +684,13 @@ func TestX509(t *testing.T) {
664684
return &authenticator.Response{User: &user.DefaultInfo{Name: "custom"}}, true, nil
665685
}),
666686

667-
ExpectUserName: "custom",
668-
ExpectOK: true,
669-
ExpectErr: false,
687+
ExpectOK: true,
688+
ExpectResponse: &authenticator.Response{
689+
User: &user.DefaultInfo{
690+
Name: "custom",
691+
},
692+
},
693+
ExpectErr: false,
670694
},
671695

672696
"future cert": {
@@ -697,9 +721,16 @@ func TestX509(t *testing.T) {
697721
Certs: getCertsFromFile(t, "client-valid", "intermediate"),
698722
User: CommonNameUserConversion,
699723

700-
ExpectUserName: "My Client",
701-
ExpectOK: true,
702-
ExpectErr: false,
724+
ExpectOK: true,
725+
ExpectResponse: &authenticator.Response{
726+
User: &user.DefaultInfo{
727+
Name: "My Client",
728+
Extra: map[string][]string{
729+
user.CredentialIDKey: {"X509SHA256=794b0529fd1a72d55d52d98be9bab5b822d16f9ae86c4373fa7beee3cafe8582"},
730+
},
731+
},
732+
},
733+
ExpectErr: false,
703734
},
704735
"multi-level, expired": {
705736
Opts: multilevelOpts,
@@ -712,42 +743,36 @@ func TestX509(t *testing.T) {
712743
}
713744

714745
for k, testCase := range testCases {
715-
req, _ := http.NewRequest("GET", "/", nil)
716-
if !testCase.Insecure {
717-
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
718-
}
719-
720-
// this effectively tests the simple dynamic verify function.
721-
a := New(testCase.Opts, testCase.User)
746+
t.Run(k, func(t *testing.T) {
747+
req, _ := http.NewRequest("GET", "/", nil)
748+
if !testCase.Insecure {
749+
req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
750+
}
722751

723-
resp, ok, err := a.AuthenticateRequest(req)
752+
// this effectively tests the simple dynamic verify function.
753+
a := New(testCase.Opts, testCase.User)
724754

725-
if testCase.ExpectErr && err == nil {
726-
t.Errorf("%s: Expected error, got none", k)
727-
continue
728-
}
729-
if !testCase.ExpectErr && err != nil {
730-
t.Errorf("%s: Got unexpected error: %v", k, err)
731-
continue
732-
}
755+
resp, ok, err := a.AuthenticateRequest(req)
733756

734-
if testCase.ExpectOK != ok {
735-
t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok)
736-
continue
737-
}
757+
if testCase.ExpectErr && err == nil {
758+
t.Fatalf("Expected error, got none")
759+
}
760+
if !testCase.ExpectErr && err != nil {
761+
t.Fatalf("Got unexpected error: %v", err)
762+
}
738763

739-
if testCase.ExpectOK {
740-
if testCase.ExpectUserName != resp.User.GetName() {
741-
t.Errorf("%s: Expected user.name=%v, got %v", k, testCase.ExpectUserName, resp.User.GetName())
764+
if testCase.ExpectOK != ok {
765+
t.Fatalf("Expected ok=%v, got %v", testCase.ExpectOK, ok)
742766
}
743767

744-
groups := resp.User.GetGroups()
745-
sort.Strings(testCase.ExpectGroups)
746-
sort.Strings(groups)
747-
if !reflect.DeepEqual(testCase.ExpectGroups, groups) {
748-
t.Errorf("%s: Expected user.groups=%v, got %v", k, testCase.ExpectGroups, groups)
768+
if testCase.ExpectOK {
769+
sort.Strings(testCase.ExpectResponse.User.GetGroups())
770+
sort.Strings(resp.User.GetGroups())
771+
if diff := cmp.Diff(testCase.ExpectResponse, resp); diff != "" {
772+
t.Errorf("Bad response; diff (-want +got)\n%s", diff)
773+
}
749774
}
750-
}
775+
})
751776
}
752777
}
753778

staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount/util.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ const (
3030
ServiceAccountUsernameSeparator = ":"
3131
ServiceAccountGroupPrefix = "system:serviceaccounts:"
3232
AllServiceAccountsGroup = "system:serviceaccounts"
33-
// CredentialIDKey is the key used in a user's "extra" to specify the unique
34-
// identifier for this identity document).
35-
CredentialIDKey = "authentication.kubernetes.io/credential-id"
3633
// IssuedCredentialIDAuditAnnotationKey is the annotation key used in the audit event that is persisted to the
3734
// '/token' endpoint for service accounts.
3835
// This annotation indicates the generated credential identifier for the service account token being issued.
@@ -150,7 +147,7 @@ func (sa *ServiceAccountInfo) UserInfo() user.Info {
150147
if info.Extra == nil {
151148
info.Extra = make(map[string][]string)
152149
}
153-
info.Extra[CredentialIDKey] = []string{sa.CredentialID}
150+
info.Extra[user.CredentialIDKey] = []string{sa.CredentialID}
154151
}
155152
if sa.NodeName != "" {
156153
if info.Extra == nil {

staging/src/k8s.io/apiserver/pkg/authentication/user/user.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ func (i *DefaultInfo) GetExtra() map[string][]string {
6666
return i.Extra
6767
}
6868

69-
// well-known user and group names
7069
const (
70+
// well-known user and group names
7171
SystemPrivilegedGroup = "system:masters"
7272
NodesGroup = "system:nodes"
7373
MonitoringGroup = "system:monitoring"
@@ -81,4 +81,8 @@ const (
8181
KubeProxy = "system:kube-proxy"
8282
KubeControllerManager = "system:kube-controller-manager"
8383
KubeScheduler = "system:kube-scheduler"
84+
85+
// CredentialIDKey is the key used in a user's "extra" to specify the unique
86+
// identifier for this identity document).
87+
CredentialIDKey = "authentication.kubernetes.io/credential-id"
8488
)

test/integration/auth/svcaccttoken_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"k8s.io/apimachinery/pkg/types"
4242
"k8s.io/apiserver/pkg/authentication/authenticator"
4343
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
44+
"k8s.io/apiserver/pkg/authentication/user"
4445
utilfeature "k8s.io/apiserver/pkg/util/feature"
4546
clientset "k8s.io/client-go/kubernetes"
4647
"k8s.io/client-go/kubernetes/scheme"
@@ -237,7 +238,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
237238
info := doTokenReview(t, cs, treq, false)
238239
// we are not testing the credential-id feature, so delete this value from the returned extra info map
239240
if info.Extra != nil {
240-
delete(info.Extra, apiserverserviceaccount.CredentialIDKey)
241+
delete(info.Extra, user.CredentialIDKey)
241242
}
242243
if len(info.Extra) > 0 {
243244
t.Fatalf("expected Extra to be empty but got: %#v", info.Extra)
@@ -309,7 +310,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
309310

310311
info := doTokenReview(t, cs, treq, false)
311312
// we are not testing the credential-id feature, so delete this value from the returned extra info map
312-
delete(info.Extra, apiserverserviceaccount.CredentialIDKey)
313+
delete(info.Extra, user.CredentialIDKey)
313314
if len(info.Extra) != 2 {
314315
t.Fatalf("expected Extra have length of 2 but was length %d: %#v", len(info.Extra), info.Extra)
315316
}
@@ -405,7 +406,7 @@ func TestServiceAccountTokenCreate(t *testing.T) {
405406

406407
info := doTokenReview(t, cs, treq, false)
407408
// we are not testing the credential-id feature, so delete this value from the returned extra info map
408-
delete(info.Extra, apiserverserviceaccount.CredentialIDKey)
409+
delete(info.Extra, user.CredentialIDKey)
409410
if len(info.Extra) != len(expectedExtraValues) {
410411
t.Fatalf("expected Extra have length of %d but was length %d: %#v", len(expectedExtraValues), len(info.Extra), info.Extra)
411412
}

0 commit comments

Comments
 (0)