mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Merge pull request #123098 from munnerz/4193-jti-audit-changes
use authentication.kubernetes.io/issued-credential-id audit annotation in serviceaccount token registry endpoint
This commit is contained in:
commit
8c6e940a97
@ -17,17 +17,28 @@ limitations under the License.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
token "k8s.io/kubernetes/pkg/serviceaccount"
|
||||
)
|
||||
|
||||
func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
|
||||
@ -38,13 +49,35 @@ func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
|
||||
DeleteCollectionWorkers: 1,
|
||||
ResourcePrefix: "serviceaccounts",
|
||||
}
|
||||
rest, err := NewREST(restOptions, nil, nil, 0, nil, nil, nil, false)
|
||||
// set issuer, podStore and secretStore to allow the token endpoint to be initialised
|
||||
rest, err := NewREST(restOptions, fakeTokenGenerator{"fake"}, nil, 0, panicGetter{}, panicGetter{}, nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error from REST storage: %v", err)
|
||||
}
|
||||
return rest, server
|
||||
}
|
||||
|
||||
// A basic fake token generator which always returns a static string
|
||||
type fakeTokenGenerator struct {
|
||||
staticToken string
|
||||
}
|
||||
|
||||
func (f fakeTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error) {
|
||||
return f.staticToken, nil
|
||||
}
|
||||
|
||||
var _ token.TokenGenerator = fakeTokenGenerator{}
|
||||
|
||||
// Currently this getter only panics as the only test case doesn't actually need the getters to function.
|
||||
// When more test cases are added, this getter will need extending/replacing to have a real test implementation.
|
||||
type panicGetter struct{}
|
||||
|
||||
func (f panicGetter) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
var _ rest.Getter = panicGetter{}
|
||||
|
||||
func validNewServiceAccount(name string) *api.ServiceAccount {
|
||||
return &api.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@ -73,6 +106,44 @@ func TestCreate(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestCreate_Token_SetsCredentialIDAuditAnnotation(t *testing.T) {
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
|
||||
// Enable JTI feature
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenJTI, true)()
|
||||
|
||||
ctx := context.Background()
|
||||
// Create a test service account
|
||||
serviceAccount := validNewServiceAccount("foo")
|
||||
// add the namespace to the context as it is required
|
||||
ctx = request.WithNamespace(ctx, serviceAccount.Namespace)
|
||||
_, err := storage.Store.Create(ctx, serviceAccount, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating test service account: %v", err)
|
||||
}
|
||||
|
||||
// create an audit context to allow recording audit information
|
||||
ctx = audit.WithAuditContext(ctx)
|
||||
_, err = storage.Token.Create(ctx, serviceAccount.Name, &authenticationapi.TokenRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: serviceAccount.Name,
|
||||
Namespace: serviceAccount.Namespace,
|
||||
},
|
||||
Spec: authenticationapi.TokenRequestSpec{ExpirationSeconds: 3600},
|
||||
}, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed calling /token endpoint for service account: %v", err)
|
||||
}
|
||||
|
||||
auditContext := audit.AuditContextFrom(ctx)
|
||||
issuedCredentialID, ok := auditContext.Event.Annotations["authentication.kubernetes.io/issued-credential-id"]
|
||||
if !ok || len(issuedCredentialID) == 0 {
|
||||
t.Errorf("did not find issued-credential-id in audit event annotations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
storage, server := newStorage(t)
|
||||
defer server.Terminate(t)
|
||||
|
@ -235,7 +235,7 @@ func (r *TokenREST) Create(ctx context.Context, name string, obj runtime.Object,
|
||||
ExpirationTimestamp: metav1.Time{Time: nowTime.Add(time.Duration(out.Spec.ExpirationSeconds) * time.Second)},
|
||||
}
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountTokenJTI) && len(sc.ID) > 0 {
|
||||
audit.AddAuditAnnotation(ctx, serviceaccount.CredentialIDKey, serviceaccount.CredentialIDForJTI(sc.ID))
|
||||
audit.AddAuditAnnotation(ctx, serviceaccount.IssuedCredentialIDAuditAnnotationKey, serviceaccount.CredentialIDForJTI(sc.ID))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
@ -39,6 +39,12 @@ const (
|
||||
// CredentialIDKey is the key used in a user's "extra" to specify the unique
|
||||
// identifier for this identity document).
|
||||
CredentialIDKey = "authentication.kubernetes.io/credential-id"
|
||||
// IssuedCredentialIDAuditAnnotationKey is the annotation key used in the audit event that is persisted to the
|
||||
// '/token' endpoint for service accounts.
|
||||
// This annotation indicates the generated credential identifier for the service account token being issued.
|
||||
// This is useful when tracing back the origin of tokens that have gone on to make request that have persisted
|
||||
// their credential-identifier into the audit log via the user's extra info stored on subsequent audit events.
|
||||
IssuedCredentialIDAuditAnnotationKey = "authentication.kubernetes.io/issued-credential-id"
|
||||
// PodNameKey is the key used in a user's "extra" to specify the pod name of
|
||||
// the authenticating request.
|
||||
PodNameKey = "authentication.kubernetes.io/pod-name"
|
||||
|
Loading…
Reference in New Issue
Block a user