Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion api/v1alpha1/authenticationconfiguration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import (
type AuthenticationConfiguration struct {
metav1.TypeMeta

JWT []apiserverv1beta1.JWTAuthenticator `json:"jwt"`
Internal Internal `json:"internal"`
JWT []apiserverv1beta1.JWTAuthenticator `json:"jwt"`
}

type Internal struct {
Prefix string `json:"prefix"`
}

func init() {
Expand Down
14 changes: 10 additions & 4 deletions api/v1alpha1/client_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package v1alpha1

import "strings"

func (c *Client) Username(prefix string) string {
func (c *Client) InternalSubject() string {
return strings.Join([]string{"client", c.Namespace, c.Name, string(c.UID)}, ":")
}

func (c *Client) Usernames(prefix string) []string {
usernames := []string{prefix + c.InternalSubject()}

if c.Spec.Username != nil {
return *c.Spec.Username
} else {
return prefix + strings.Join([]string{"client", c.Namespace, c.Name, string(c.UID)}, ":")
usernames = append(usernames, *c.Spec.Username)
}

return usernames
}
14 changes: 10 additions & 4 deletions api/v1alpha1/exporter_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package v1alpha1

import "strings"

func (e *Exporter) Username(prefix string) string {
func (e *Exporter) InternalSubject() string {
return strings.Join([]string{"exporter", e.Namespace, e.Name, string(e.UID)}, ":")
}

func (e *Exporter) Usernames(prefix string) []string {
usernames := []string{prefix + e.InternalSubject()}

if e.Spec.Username != nil {
return *e.Spec.Username
} else {
return prefix + strings.Join([]string{"exporter", e.Namespace, e.Name, string(e.UID)}, ":")
usernames = append(usernames, *e.Spec.Username)
}

return usernames
}
16 changes: 16 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,13 @@ func main() {
[]byte(os.Getenv("CONTROLLER_KEY")),
"https://localhost:8085",
"jumpstarter",
"internal:",
)
if err != nil {
setupLog.Error(err, "unable to create internal oidc signer")
os.Exit(1)
}

authenticator, err := config.LoadConfiguration(
authenticator, prefix, err := config.LoadConfiguration(
context.Background(),
mgr.GetAPIReader(),
mgr.GetScheme(),
Expand Down Expand Up @@ -206,7 +205,7 @@ func main() {
Client: watchClient,
Scheme: mgr.GetScheme(),
Authn: authentication.NewBearerTokenAuthenticator(authenticator),
Authz: authorization.NewBasicAuthorizer(watchClient, oidcSigner.Prefix()),
Authz: authorization.NewBasicAuthorizer(watchClient, prefix),
Attr: authorization.NewMetadataAttributesGetter(authorization.MetadataAttributesGetterConfig{
NamespaceKey: "jumpstarter-namespace",
ResourceKey: "jumpstarter-kind",
Expand Down
5 changes: 3 additions & 2 deletions internal/authorization/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package authorization

import (
"context"
"slices"

jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1"
"k8s.io/apiserver/pkg/authorization/authorizer"
Expand Down Expand Up @@ -30,7 +31,7 @@ func (b *BasicAuthorizer) Authorize(
}, &e); err != nil {
return authorizer.DecisionDeny, "failed to get exporter", err
}
if e.Username(b.prefix) == attributes.GetUser().GetName() {
if slices.Contains(e.Usernames(b.prefix), attributes.GetUser().GetName()) {
return authorizer.DecisionAllow, "", nil
} else {
return authorizer.DecisionDeny, "", nil
Expand All @@ -43,7 +44,7 @@ func (b *BasicAuthorizer) Authorize(
}, &c); err != nil {
return authorizer.DecisionDeny, "failed to get client", err
}
if c.Username(b.prefix) == attributes.GetUser().GetName() {
if slices.Contains(c.Usernames(b.prefix), attributes.GetUser().GetName()) {
return authorizer.DecisionAllow, "", nil
} else {
return authorizer.DecisionDeny, "", nil
Expand Down
12 changes: 6 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ func LoadConfiguration(
key client.ObjectKey,
signer *oidc.Signer,
certificateAuthority string,
) (authenticator.Token, error) {
) (authenticator.Token, string, error) {
var configmap corev1.ConfigMap
if err := client.Get(ctx, key, &configmap); err != nil {
return nil, err
return nil, "", err
}

rawAuthenticationConfiguration, ok := configmap.Data["authentication"]
if !ok {
return nil, fmt.Errorf("LoadConfiguration: missing authentication section")
return nil, "", fmt.Errorf("LoadConfiguration: missing authentication section")
}

authenticator, err := oidc.LoadAuthenticationConfiguration(
authenticator, prefix, err := oidc.LoadAuthenticationConfiguration(
ctx,
scheme,
[]byte(rawAuthenticationConfiguration),
signer,
certificateAuthority,
)
if err != nil {
return nil, err
return nil, "", err
}

return authenticator, nil
return authenticator, prefix, nil
}
2 changes: 1 addition & 1 deletion internal/controller/client_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (r *ClientReconciler) reconcileStatusCredential(
secret, err := ensureSecret(ctx, kclient.ObjectKey{
Name: client.Name + "-client",
Namespace: client.Namespace,
}, r.Client, r.Signer, client.Username(r.Signer.Prefix()))
}, r.Client, r.Signer, client.InternalSubject())
if err != nil {
return fmt.Errorf("reconcileStatusCredential: failed to prepare credential for client: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/controller/client_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var _ = Describe("Identity Controller", func() {
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ClientReconciler{
Expand All @@ -98,7 +98,7 @@ var _ = Describe("Identity Controller", func() {

It("should reconcile a missing token secret", func() {
By("recreating the secret")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ClientReconciler{
Expand Down Expand Up @@ -129,7 +129,7 @@ var _ = Describe("Identity Controller", func() {

It("should reconcile an invalid token secret", func() {
By("recreating the secret")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ClientReconciler{
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/exporter_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (r *ExporterReconciler) reconcileStatusCredential(
secret, err := ensureSecret(ctx, client.ObjectKey{
Name: exporter.Name + "-exporter",
Namespace: exporter.Namespace,
}, r.Client, r.Signer, exporter.Username(r.Signer.Prefix()))
}, r.Client, r.Signer, exporter.InternalSubject())
if err != nil {
return fmt.Errorf("reconcileStatusCredential: failed to prepare credential for exporter: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/exporter_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var _ = Describe("Exporter Controller", func() {
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ExporterReconciler{
Expand All @@ -97,7 +97,7 @@ var _ = Describe("Exporter Controller", func() {
})
It("should reconcile a missing token secret", func() {
By("recreating the secret")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ExporterReconciler{
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/lease_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ func reconcileLease(ctx context.Context, lease *jumpstarterdevv1alpha1.Lease) re
Scheme: k8sClient.Scheme(),
}

signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

exporterReconciler := &ExporterReconciler{
Expand Down
8 changes: 4 additions & 4 deletions internal/controller/secret_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func ensureSecret(
key client.ObjectKey,
kclient client.Client,
signer *oidc.Signer,
username string,
subject string,
) (*corev1.Secret, error) {
logger := log.FromContext(ctx).WithName("ensureSecret")
var secret corev1.Secret
Expand All @@ -29,7 +29,7 @@ func ensureSecret(
}
// Secret not present
logger.Info("secret not present, creating")
token, err := signer.Token(username)
token, err := signer.Token(subject)
if err != nil {
logger.Error(err, "failed to sign token")
return nil, err
Expand All @@ -51,11 +51,11 @@ func ensureSecret(
return &secret, nil
} else {
token, ok := secret.Data[TokenKey]
if !ok || signer.UnsafeValidate(string(token)) != nil {
if !ok || signer.Validate(string(token)) != nil {
// Secret present but invalid
logger.Info("secret present but invalid, updating")
original := client.MergeFrom(secret.DeepCopy())
token, err := signer.Token(username)
token, err := signer.Token(subject)
if err != nil {
logger.Error(err, "failed to sign token")
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func createExporters(ctx context.Context, exporters ...*jumpstarterdevv1alpha1.E
Namespace: "default", // TODO(user):Modify as needed
}

signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:")
signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy")
Expect(err).NotTo(HaveOccurred())

controllerReconciler := &ExporterReconciler{
Expand Down
17 changes: 12 additions & 5 deletions internal/oidc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
"k8s.io/utils/ptr"
)

func LoadAuthenticationConfiguration(
Expand All @@ -21,15 +20,19 @@ func LoadAuthenticationConfiguration(
configuration []byte,
signer *Signer,
certificateAuthority string,
) (authenticator.Token, error) {
) (authenticator.Token, string, error) {
var authenticationConfiguration jumpstarterdevv1alpha1.AuthenticationConfiguration
if err := runtime.DecodeInto(
serializer.NewCodecFactory(scheme, serializer.EnableStrict).
UniversalDecoder(jumpstarterdevv1alpha1.GroupVersion),
configuration,
&authenticationConfiguration,
); err != nil {
return nil, err
return nil, "", err
}

if authenticationConfiguration.Internal.Prefix == "" {
authenticationConfiguration.Internal.Prefix = "internal:"
}

authenticationConfiguration.JWT = append(authenticationConfiguration.JWT, apiserverv1beta1.JWTAuthenticator{
Expand All @@ -41,16 +44,20 @@ func LoadAuthenticationConfiguration(
ClaimMappings: apiserverv1beta1.ClaimMappings{
Username: apiserverv1beta1.PrefixedClaimOrExpression{
Claim: "sub",
Prefix: ptr.To(signer.Prefix()),
Prefix: &authenticationConfiguration.Internal.Prefix,
},
},
})

return newJWTAuthenticator(
authn, err := newJWTAuthenticator(
ctx,
scheme,
authenticationConfiguration,
)
if err != nil {
return nil, "", err
}
return authn, authenticationConfiguration.Internal.Prefix, nil
}

// Reference: https://github.com/kubernetes/kubernetes/blob/v1.32.1/pkg/kubeapiserver/authenticator/config.go#L244
Expand Down
Loading