From 012ee1ce0116ba705ee62d1d4d620509c65d9477 Mon Sep 17 00:00:00 2001 From: Nick Cao Date: Thu, 30 Jan 2025 14:19:41 -0500 Subject: [PATCH 1/2] Implement OIDC based authentication and authorization --- PROJECT | 7 + .../authenticationconfiguration_types.go | 19 ++ api/v1alpha1/client_types.go | 3 +- api/v1alpha1/exporter_types.go | 3 +- api/v1alpha1/exporteraccesspolicy_types.go | 72 ++++++ api/v1alpha1/lease_types.go | 2 + api/v1alpha1/zz_generated.deepcopy.go | 186 ++++++++++++++- cmd/main.go | 63 +++++ cmd/mock/main.go | 107 --------- config/samples/dex.yaml | 28 +++ config/samples/kustomization.yaml | 1 + config/samples/v1alpha1_client.yaml | 1 + .../v1alpha1_exporteraccesspolicy.yaml | 30 +++ .../templates/cms/controller-cm.yaml | 39 ++++ .../crds/jumpstarter.dev_clients.yaml | 3 + ...umpstarter.dev_exporteraccesspolicies.yaml | 166 +++++++++++++ .../crds/jumpstarter.dev_exporters.yaml | 3 + .../crds/jumpstarter.dev_leases.yaml | 4 + .../templates/rbac/role.yaml | 8 + go.mod | 99 +++++--- go.sum | 219 ++++++++++++------ .../{service => authentication}/bearer.go | 22 +- internal/authentication/types.go | 11 + internal/authorization/basic.go | 72 ++++++ internal/authorization/metadata.go | 72 ++++++ internal/authorization/types.go | 12 + internal/config/config.go | 44 ++++ internal/controller/client_controller.go | 10 +- internal/controller/client_controller_test.go | 7 +- internal/controller/exporter_controller.go | 11 +- .../controller/exporter_controller_test.go | 7 +- internal/controller/lease_controller.go | 84 ++++++- internal/controller/lease_controller_test.go | 5 + internal/controller/suite_test.go | 7 +- internal/controller/token.go | 124 ---------- internal/oidc/config.go | 90 +++++++ internal/oidc/op.go | 106 +++++++++ internal/oidc/token.go | 105 +++++++++ internal/service/controller_service.go | 39 ++-- internal/service/oidc_service.go | 39 ++++ internal/service/router_service.go | 3 +- 41 files changed, 1543 insertions(+), 390 deletions(-) create mode 100644 api/v1alpha1/authenticationconfiguration_types.go create mode 100644 api/v1alpha1/exporteraccesspolicy_types.go delete mode 100644 cmd/mock/main.go create mode 100644 config/samples/dex.yaml create mode 100644 config/samples/v1alpha1_exporteraccesspolicy.yaml create mode 100644 deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml create mode 100644 deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml rename internal/{service => authentication}/bearer.go (66%) create mode 100644 internal/authentication/types.go create mode 100644 internal/authorization/basic.go create mode 100644 internal/authorization/metadata.go create mode 100644 internal/authorization/types.go create mode 100644 internal/config/config.go delete mode 100644 internal/controller/token.go create mode 100644 internal/oidc/config.go create mode 100644 internal/oidc/op.go create mode 100644 internal/oidc/token.go create mode 100644 internal/service/oidc_service.go diff --git a/PROJECT b/PROJECT index f4188f5c..fce14fae 100644 --- a/PROJECT +++ b/PROJECT @@ -28,4 +28,11 @@ resources: domain: jumpstarter.dev kind: Lease version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + domain: jumpstarter.dev + kind: ExporterAccessPolicy + path: github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/authenticationconfiguration_types.go b/api/v1alpha1/authenticationconfiguration_types.go new file mode 100644 index 00000000..8088b27f --- /dev/null +++ b/api/v1alpha1/authenticationconfiguration_types.go @@ -0,0 +1,19 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apiserverv1beta1 "k8s.io/apiserver/pkg/apis/apiserver/v1beta1" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// AuthenticationConfiguration provides versioned configuration for authentication. +type AuthenticationConfiguration struct { + metav1.TypeMeta + + JWT []apiserverv1beta1.JWTAuthenticator `json:"jwt"` +} + +func init() { + SchemeBuilder.Register(&AuthenticationConfiguration{}) +} diff --git a/api/v1alpha1/client_types.go b/api/v1alpha1/client_types.go index f59debba..a278dcd8 100644 --- a/api/v1alpha1/client_types.go +++ b/api/v1alpha1/client_types.go @@ -26,8 +26,7 @@ import ( // ClientSpec defines the desired state of Identity type ClientSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + Username *string `json:"username,omitempty"` } // ClientStatus defines the observed state of Identity diff --git a/api/v1alpha1/exporter_types.go b/api/v1alpha1/exporter_types.go index 783ac320..662550f6 100644 --- a/api/v1alpha1/exporter_types.go +++ b/api/v1alpha1/exporter_types.go @@ -26,8 +26,7 @@ import ( // ExporterSpec defines the desired state of Exporter type ExporterSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + Username *string `json:"username,omitempty"` } // ExporterStatus defines the observed state of Exporter diff --git a/api/v1alpha1/exporteraccesspolicy_types.go b/api/v1alpha1/exporteraccesspolicy_types.go new file mode 100644 index 00000000..4e10d262 --- /dev/null +++ b/api/v1alpha1/exporteraccesspolicy_types.go @@ -0,0 +1,72 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +type From struct { + ClientSelector metav1.LabelSelector `json:"clientSelector,omitempty"` +} + +type Policy struct { + Priority int `json:"priority,omitempty"` + From []From `json:"from,omitempty"` + MaximumDuration *metav1.Duration `json:"maximumDuration,omitempty"` + SpotAccess bool `json:"spotAccess,omitempty"` +} + +// ExporterAccessPolicySpec defines the desired state of ExporterAccessPolicy. +type ExporterAccessPolicySpec struct { + ExporterSelector metav1.LabelSelector `json:"exporterSelector,omitempty"` + Policies []Policy `json:"policies,omitempty"` +} + +// ExporterAccessPolicyStatus defines the observed state of ExporterAccessPolicy. +type ExporterAccessPolicyStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// ExporterAccessPolicy is the Schema for the exporteraccesspolicies API. +type ExporterAccessPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ExporterAccessPolicySpec `json:"spec,omitempty"` + Status ExporterAccessPolicyStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ExporterAccessPolicyList contains a list of ExporterAccessPolicy. +type ExporterAccessPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ExporterAccessPolicy `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ExporterAccessPolicy{}, &ExporterAccessPolicyList{}) +} diff --git a/api/v1alpha1/lease_types.go b/api/v1alpha1/lease_types.go index f6c231ba..f8434c71 100644 --- a/api/v1alpha1/lease_types.go +++ b/api/v1alpha1/lease_types.go @@ -41,6 +41,8 @@ type LeaseStatus struct { EndTime *metav1.Time `json:"endTime,omitempty"` ExporterRef *corev1.LocalObjectReference `json:"exporterRef,omitempty"` Ended bool `json:"ended"` + Priority int `json:"priority,omitempty"` + SpotAccess bool `json:"spotAccess,omitempty"` Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index bf0e9c5e..7f21fd38 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -24,14 +24,46 @@ import ( "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/apis/apiserver/v1beta1" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AuthenticationConfiguration) DeepCopyInto(out *AuthenticationConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.JWT != nil { + in, out := &in.JWT, &out.JWT + *out = make([]v1beta1.JWTAuthenticator, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AuthenticationConfiguration. +func (in *AuthenticationConfiguration) DeepCopy() *AuthenticationConfiguration { + if in == nil { + return nil + } + out := new(AuthenticationConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AuthenticationConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Client) DeepCopyInto(out *Client) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -88,6 +120,11 @@ func (in *ClientList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClientSpec) DeepCopyInto(out *ClientSpec) { *out = *in + if in.Username != nil { + in, out := &in.Username, &out.Username + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientSpec. @@ -152,7 +189,7 @@ func (in *Exporter) DeepCopyInto(out *Exporter) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -174,6 +211,103 @@ func (in *Exporter) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterAccessPolicy) DeepCopyInto(out *ExporterAccessPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterAccessPolicy. +func (in *ExporterAccessPolicy) DeepCopy() *ExporterAccessPolicy { + if in == nil { + return nil + } + out := new(ExporterAccessPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ExporterAccessPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterAccessPolicyList) DeepCopyInto(out *ExporterAccessPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ExporterAccessPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterAccessPolicyList. +func (in *ExporterAccessPolicyList) DeepCopy() *ExporterAccessPolicyList { + if in == nil { + return nil + } + out := new(ExporterAccessPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ExporterAccessPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterAccessPolicySpec) DeepCopyInto(out *ExporterAccessPolicySpec) { + *out = *in + in.ExporterSelector.DeepCopyInto(&out.ExporterSelector) + if in.Policies != nil { + in, out := &in.Policies, &out.Policies + *out = make([]Policy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterAccessPolicySpec. +func (in *ExporterAccessPolicySpec) DeepCopy() *ExporterAccessPolicySpec { + if in == nil { + return nil + } + out := new(ExporterAccessPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExporterAccessPolicyStatus) DeepCopyInto(out *ExporterAccessPolicyStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterAccessPolicyStatus. +func (in *ExporterAccessPolicyStatus) DeepCopy() *ExporterAccessPolicyStatus { + if in == nil { + return nil + } + out := new(ExporterAccessPolicyStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExporterList) DeepCopyInto(out *ExporterList) { *out = *in @@ -209,6 +343,11 @@ func (in *ExporterList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExporterSpec) DeepCopyInto(out *ExporterSpec) { *out = *in + if in.Username != nil { + in, out := &in.Username, &out.Username + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExporterSpec. @@ -260,6 +399,22 @@ func (in *ExporterStatus) DeepCopy() *ExporterStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *From) DeepCopyInto(out *From) { + *out = *in + in.ClientSelector.DeepCopyInto(&out.ClientSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new From. +func (in *From) DeepCopy() *From { + if in == nil { + return nil + } + out := new(From) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Lease) DeepCopyInto(out *Lease) { *out = *in @@ -371,3 +526,30 @@ func (in *LeaseStatus) DeepCopy() *LeaseStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Policy) DeepCopyInto(out *Policy) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]From, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.MaximumDuration != nil { + in, out := &in.MaximumDuration, &out.MaximumDuration + *out = new(metav1.Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy. +func (in *Policy) DeepCopy() *Policy { + if in == nil { + return nil + } + out := new(Policy) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/main.go b/cmd/main.go index 09c095df..b8fa155a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,12 +17,16 @@ limitations under the License. package main import ( + "context" "crypto/tls" + "encoding/pem" "flag" + "net" "os" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + apiserverinstall "k8s.io/apiserver/pkg/apis/apiserver/install" _ "k8s.io/client-go/plugin/pkg/client/auth" "k8s.io/apimachinery/pkg/runtime" @@ -36,7 +40,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authentication" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authorization" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/config" "github.com/jumpstarter-dev/jumpstarter-controller/internal/controller" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" "github.com/jumpstarter-dev/jumpstarter-controller/internal/service" // +kubebuilder:scaffold:imports ) @@ -50,7 +58,9 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(jumpstarterdevv1alpha1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme + apiserverinstall.Install(scheme) } func main() { @@ -125,9 +135,46 @@ func main() { os.Exit(1) } + oidcCert, err := service.NewSelfSignedCertificate("jumpstarter oidc", []string{"localhost"}, []net.IP{}) + if err != nil { + setupLog.Error(err, "unable to generate certificate for internal oidc provider") + os.Exit(1) + } + + oidcSigner, err := oidc.NewSignerFromSeed( + []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( + context.Background(), + mgr.GetAPIReader(), + mgr.GetScheme(), + client.ObjectKey{ + Namespace: os.Getenv("NAMESPACE"), + Name: "jumpstarter-controller", + }, + oidcSigner, + string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE", + Bytes: oidcCert.Certificate[0], + })), + ) + if err != nil { + setupLog.Error(err, "unable to load configuration") + os.Exit(1) + } + if err = (&controller.ExporterReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + Signer: oidcSigner, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Exporter") os.Exit(1) @@ -135,6 +182,7 @@ func main() { if err = (&controller.ClientReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), + Signer: oidcSigner, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Identity") os.Exit(1) @@ -157,6 +205,13 @@ func main() { if err = (&service.ControllerService{ Client: watchClient, Scheme: mgr.GetScheme(), + Authn: authentication.NewBearerTokenAuthenticator(authenticator), + Authz: authorization.NewBasicAuthorizer(watchClient, oidcSigner.Prefix()), + Attr: authorization.NewMetadataAttributesGetter(authorization.MetadataAttributesGetterConfig{ + NamespaceKey: "jumpstarter-namespace", + ResourceKey: "jumpstarter-kind", + NameKey: "jumpstarter-name", + }), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create service", "service", "Controller") os.Exit(1) @@ -170,6 +225,14 @@ func main() { os.Exit(1) } + if err = (&service.OIDCService{ + Signer: oidcSigner, + Cert: oidcCert, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create service", "service", "Dashboard") + os.Exit(1) + } + if err = (&service.DashboardService{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/cmd/mock/main.go b/cmd/mock/main.go deleted file mode 100644 index 5759d699..00000000 --- a/cmd/mock/main.go +++ /dev/null @@ -1,107 +0,0 @@ -package main - -import ( - "log" - "net" - "os" - - jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" - "github.com/jumpstarter-dev/jumpstarter-controller/internal/controller" - pb "github.com/jumpstarter-dev/jumpstarter-controller/internal/protocol/jumpstarter/v1" - "github.com/jumpstarter-dev/jumpstarter-controller/internal/service" - "google.golang.org/grpc" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -var ( - scheme = runtime.NewScheme() -) - -const ( - // to make sure we are not hardcoding namespaces in code - namespace = "81c6ed4dc0bf88203081454aefa806ca" -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(jumpstarterdevv1alpha1.AddToScheme(scheme)) - - _ = os.Setenv("NAMESPACE", namespace) - _ = os.Setenv("CONTROLLER_KEY", "dummy") - _ = os.Setenv("ROUTER_KEY", "dummy") -} - -func main() { - server := grpc.NewServer() - - exporter := jumpstarterdevv1alpha1.Exporter{ - ObjectMeta: metav1.ObjectMeta{ - Name: "exporter-sample", - Namespace: namespace, - }, - Status: jumpstarterdevv1alpha1.ExporterStatus{ - Credential: &corev1.LocalObjectReference{ - Name: "exporter-sample-token", - }, - }, - } - - exporterToken, err := controller.SignObjectToken( - "https://jumpstarter.dev/controller", - []string{"https://jumpstarter.dev/controller"}, - &exporter, - scheme, - ) - utilruntime.Must(err) - - log.Println("exporter token:", exporterToken) - - client := jumpstarterdevv1alpha1.Client{ - ObjectMeta: metav1.ObjectMeta{ - Name: "identity-sample", - Namespace: namespace, - }, - Status: jumpstarterdevv1alpha1.ClientStatus{ - Credential: &corev1.LocalObjectReference{ - Name: "identity-sample-token", - }, - }, - } - - clientToken, err := controller.SignObjectToken( - "https://jumpstarter.dev/controller", - []string{"https://jumpstarter.dev/controller"}, - &client, - scheme, - ) - utilruntime.Must(err) - - log.Println("client token:", clientToken) - - c := fake.NewClientBuilder().WithScheme(scheme).WithObjects( - &exporter, - &client, - ).WithStatusSubresource(&exporter).Build() - - pb.RegisterControllerServiceServer(server, &service.ControllerService{ - Client: c, - Scheme: scheme, - }) - - pb.RegisterRouterServiceServer(server, &service.RouterService{ - Client: c, - Scheme: scheme, - }) - - listener, err := net.Listen("tcp", ":8083") - if err != nil { - log.Fatal(err) - } - - log.Fatal(server.Serve(listener)) -} diff --git a/config/samples/dex.yaml b/config/samples/dex.yaml new file mode 100644 index 00000000..f0df869f --- /dev/null +++ b/config/samples/dex.yaml @@ -0,0 +1,28 @@ +issuer: https://10.239.206.8:5556/dex +storage: + type: sqlite3 + config: + file: dex.db +web: + https: 0.0.0.0:5556 + tlsCert: 10.239.206.8/cert.pem + tlsKey: 10.239.206.8/key.pem +logger: + level: "debug" + format: "text" +staticClients: + - id: jumpstarter + name: jumpstarter + secret: secret +oauth2: + passwordConnector: local +enablePasswordDB: true +staticPasswords: + - email: "client-sample@example.com" + hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" # password + username: "client-sample" + userID: "73bca0b9-9be6-4e73-a8fb-347c2ac23255" + - email: "exporter-sample@example.com" + hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" # password + username: "exporter-sample" + userID: "a4cb4de2-4467-4e5c-a42a-33be8783649d" diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index a1013098..cb8b3f07 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -3,4 +3,5 @@ resources: - v1alpha1_exporter.yaml - v1alpha1_client.yaml - v1alpha1_lease.yaml +- v1alpha1_exporteraccesspolicy.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/v1alpha1_client.yaml b/config/samples/v1alpha1_client.yaml index 8e5352b7..76c4adde 100644 --- a/config/samples/v1alpha1_client.yaml +++ b/config/samples/v1alpha1_client.yaml @@ -4,5 +4,6 @@ metadata: labels: app.kubernetes.io/name: jumpstarter-router app.kubernetes.io/managed-by: kustomize + client-type: developer name: client-sample spec: {} diff --git a/config/samples/v1alpha1_exporteraccesspolicy.yaml b/config/samples/v1alpha1_exporteraccesspolicy.yaml new file mode 100644 index 00000000..f14c0002 --- /dev/null +++ b/config/samples/v1alpha1_exporteraccesspolicy.yaml @@ -0,0 +1,30 @@ +apiVersion: jumpstarter.dev/v1alpha1 +kind: ExporterAccessPolicy +metadata: + labels: + app.kubernetes.io/name: jumpstarter-router + app.kubernetes.io/managed-by: kustomize + name: default +spec: + exporterSelector: + matchLabels: + dut: fancy-hardware + policies: + - priority: 20 # Administrators come first, highest priority + from: + - clientSelector: + matchLabels: + client-type: administrator + - priority: 10 # Developers come next, maximum 2days + maximumDuration: 24h + from: + - clientSelector: + matchLabels: + client-type: developer + - priority: 5 # CI comes next, but only spot instances, can be deallocated + maximumDuration: 12h + spotAccess: true + from: + - clientSelector: + matchLabels: + client-type: ci diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml new file mode 100644 index 00000000..b9019cc5 --- /dev/null +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/cms/controller-cm.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: jumpstarter-controller + namespace: {{ default .Release.Namespace .Values.namespace }} + labels: + control-plane: controller-manager + app.kubernetes.io/name: jumpstarter-controller + {{ if .Values.global.timestamp }} + deployment.timestamp: {{ .Values.global.timestamp | quote }} + {{ end }} +data: + authentication: | + apiVersion: jumpstarter.dev/v1alpha1 + kind: AuthenticationConfiguration + # jwt: + # - issuer: + # url: https://10.239.206.8:5556/dex + # audiences: + # - jumpstarter + # audienceMatchPolicy: MatchAny + # certificateAuthority: | + # -----BEGIN CERTIFICATE----- + # MIIB/DCCAYKgAwIBAgIIcpC2uS+SjEIwCgYIKoZIzj0EAwMwIDEeMBwGA1UEAxMV + # bWluaWNhIHJvb3QgY2EgNzI5MGI2MCAXDTI1MDIwMzE5MzMyNVoYDzIxMjUwMjAz + # MTkzMzI1WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA3MjkwYjYwdjAQBgcq + # hkjOPQIBBgUrgQQAIgNiAAQzezKJ4My35HPeoJvvzTjhS2uJMBYrYfrs5csxZjiy + # q8ORrHM539XhWlA6sVZODhzcF2KL4mC9xKz/yIrsws+LKsIWNHGGmIPEKFYnHBGw + # VBGeARvhpzZP/9frJXAN/8ejgYYwgYMwDgYDVR0PAQH/BAQDAgKEMB0GA1UdJQQW + # MBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud + # DgQWBBSZRBCUuP3ta2xsfjnWIjvgvz4fojAfBgNVHSMEGDAWgBSZRBCUuP3ta2xs + # fjnWIjvgvz4fojAKBggqhkjOPQQDAwNoADBlAjADql5Ks5wh181iUa1ZBnx4XOVe + # l0l7I+mwlwJSPmkZHxruWZTx7gQU4tfDCr+UuzUCMQC2aDXRb17cphipK4gzbExv + # EDLExjhHAqMPrKDmT0jHIi7Bbos38/1tyZ/IoKjLnv0= + # -----END CERTIFICATE----- + # claimMappings: + # username: + # claim: "sub" + # prefix: "" diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml index eccf34a0..ae94bdfe 100644 --- a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_clients.yaml @@ -38,6 +38,9 @@ spec: type: object spec: description: ClientSpec defines the desired state of Identity + properties: + username: + type: string type: object status: description: ClientStatus defines the observed state of Identity diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml new file mode 100644 index 00000000..ec1b7878 --- /dev/null +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporteraccesspolicies.yaml @@ -0,0 +1,166 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: exporteraccesspolicies.jumpstarter.dev +spec: + group: jumpstarter.dev + names: + kind: ExporterAccessPolicy + listKind: ExporterAccessPolicyList + plural: exporteraccesspolicies + singular: exporteraccesspolicy + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ExporterAccessPolicy is the Schema for the exporteraccesspolicies + API. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ExporterAccessPolicySpec defines the desired state of ExporterAccessPolicy. + properties: + exporterSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + policies: + items: + properties: + from: + items: + properties: + clientSelector: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: array + maximumDuration: + type: string + priority: + type: integer + spotAccess: + type: boolean + type: object + type: array + type: object + status: + description: ExporterAccessPolicyStatus defines the observed state of + ExporterAccessPolicy. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml index 145936ca..5c0d0c66 100644 --- a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_exporters.yaml @@ -38,6 +38,9 @@ spec: type: object spec: description: ExporterSpec defines the desired state of Exporter + properties: + username: + type: string type: object status: description: ExporterStatus defines the observed state of Exporter diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml index a1633ed8..30fde959 100644 --- a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/crds/jumpstarter.dev_leases.yaml @@ -207,6 +207,10 @@ spec: type: string type: object x-kubernetes-map-type: atomic + priority: + type: integer + spotAccess: + type: boolean required: - ended type: object diff --git a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml index bba3978c..bc5f51d6 100644 --- a/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml +++ b/deploy/helm/jumpstarter/charts/jumpstarter-controller/templates/rbac/role.yaml @@ -46,3 +46,11 @@ rules: - get - patch - update +- apiGroups: + - jumpstarter.dev + resources: + - exporteraccesspolicies + verbs: + - get + - list + - watch diff --git a/go.mod b/go.mod index dd81269a..8ac3e8e2 100644 --- a/go.mod +++ b/go.mod @@ -3,43 +3,55 @@ module github.com/jumpstarter-dev/jumpstarter-controller go 1.22.3 require ( + filippo.io/keygen v0.0.0-20240718133620-7f162efbbd87 github.com/gin-gonic/gin v1.10.0 + github.com/go-jose/go-jose/v4 v4.0.4 + github.com/go-logr/logr v1.4.2 github.com/golang-jwt/jwt/v5 v5.2.1 - github.com/onsi/ginkgo/v2 v2.20.2 - github.com/onsi/gomega v1.34.1 + github.com/onsi/ginkgo/v2 v2.22.2 + github.com/onsi/gomega v1.36.2 github.com/spf13/cobra v1.8.1 - golang.org/x/sync v0.8.0 - google.golang.org/grpc v1.66.2 - google.golang.org/protobuf v1.34.2 + github.com/zitadel/oidc/v3 v3.34.1 + golang.org/x/sync v0.10.0 + google.golang.org/grpc v1.70.0 + google.golang.org/protobuf v1.36.4 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.31.1 - k8s.io/apimachinery v0.31.1 - k8s.io/cli-runtime v0.31.1 - k8s.io/client-go v0.31.1 - sigs.k8s.io/controller-runtime v0.19.0 + k8s.io/api v0.31.5 + k8s.io/apimachinery v0.31.5 + k8s.io/apiserver v0.31.5 + k8s.io/cli-runtime v0.31.5 + k8s.io/client-go v0.31.5 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + sigs.k8s.io/controller-runtime v0.19.4 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect + filippo.io/bigmod v0.0.3 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.8.0 // indirect github.com/bytedance/sonic v1.11.6 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect + github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.12.1 // indirect - github.com/evanphx/json-patch v5.9.0+incompatible // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-chi/chi/v5 v5.2.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect @@ -48,16 +60,17 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect github.com/google/uuid v1.6.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect @@ -66,38 +79,50 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/muhlemmer/gu v0.3.1 // indirect + github.com/muhlemmer/httpforwarded v0.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.20.3 // indirect + github.com/pquerna/cachecontrol v0.1.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.59.1 // indirect + github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/zitadel/logging v0.6.1 // indirect + github.com/zitadel/schema v1.3.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.25.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.28.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/component-base v0.31.5 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect - k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index b4f63727..3a6deddb 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,45 @@ -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= -github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +filippo.io/bigmod v0.0.3 h1:qmdCFHmEMS+PRwzrW6eUrgA4Q3T8D6bRcjsypDMtWHM= +filippo.io/bigmod v0.0.3/go.mod h1:WxGvOYE0OUaBC2N112Dflb3CjOnMBuNRA2UWZc2UbPE= +filippo.io/keygen v0.0.0-20240718133620-7f162efbbd87 h1:HlcHAMbI9Xvw3aWnhPngghMl5AKE2GOvjmvSGOKzCcI= +filippo.io/keygen v0.0.0-20240718133620-7f162efbbd87/go.mod h1:nAs0+DyACEQGudhkTwlPC9atyqDYC7ZotgZR7D8OwXM= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ= +github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3 h1:oe6fCvaEpkhyW3qAicT0TnGtyht/UrgvOwMcEgLb7Aw= +github.com/canonical/go-sp800.90a-drbg v0.0.0-20210314144037-6eeb1040d6c3/go.mod h1:qdP0gaj0QtgX2RUZhnlVrceJ+Qln8aSlDyJwelLLFeM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= +github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= -github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= -github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -35,16 +52,24 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -63,8 +88,12 @@ github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17w github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -73,12 +102,14 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= -github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -87,18 +118,17 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= @@ -114,12 +144,16 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= +github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= +github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY= +github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= -github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= +github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -127,33 +161,44 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4= -github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= +github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= -github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -162,71 +207,92 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zitadel/logging v0.6.1 h1:Vyzk1rl9Kq9RCevcpX6ujUaTYFX43aa4LkvV1TvUk+Y= +github.com/zitadel/logging v0.6.1/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= +github.com/zitadel/oidc/v3 v3.34.1 h1:/rxx2HxEowd8Sdb8sxcRxTu9pLy3/TXBLrewKOUMTHA= +github.com/zitadel/oidc/v3 v3.34.1/go.mod h1:lhAdAP1iWAnpfWF8CWNiO6yKvGFtPMuAubPwP5JC7Ec= +github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= +github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= +golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= -google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -234,32 +300,39 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= -k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= -k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= -k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk= -k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U= -k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= -k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= +k8s.io/api v0.31.5 h1:7jP74egbPUOCLJV5KheUnwo9gz3zzUsMIj2EPkuYK1E= +k8s.io/api v0.31.5/go.mod h1:RMyMdZG1kJjou2ng5buEti0OHlo0uFXgSzTZ/k5LeVk= +k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= +k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apimachinery v0.31.5 h1:NxhAVGcfrSdTMx3M2v1OnvcMS7h1ZnWyt2x2z8CJJBU= +k8s.io/apimachinery v0.31.5/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apiserver v0.31.5 h1:n0daI1zIb+G2Jkzqjm2NQJSJfTKccgFeHHQM4LYsz7E= +k8s.io/apiserver v0.31.5/go.mod h1:SboTZ2NHCsXjAHqTrE/kDTnrzquVY5mDKNnoCdRFLJw= +k8s.io/cli-runtime v0.31.5 h1:d8Q1CW6u8DWlU+wZgs7gQXeEl5J9rAHU4uDUz2uFtFY= +k8s.io/cli-runtime v0.31.5/go.mod h1:8zdTB5+NadarkrlOLzboMNxBjrTs6O2alRIokk9mzEY= +k8s.io/client-go v0.31.5 h1:rmDswcUaIFAJ5vJaB82pjyqc52DgHCPv0G6af3OupO0= +k8s.io/client-go v0.31.5/go.mod h1:js93IlRSzRHql9o9zP54N56rMR249uH4+srnSOcFLsU= +k8s.io/component-base v0.31.5 h1:kpFiy1hI7F4Owp+o59H2CVLzmN94qwcPz+2L6wRhkqM= +k8s.io/component-base v0.31.5/go.mod h1:OiiusrmcLz42i9VvcAd94yQIN7UzQHJxN/hXxwYzj6E= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= -k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 h1:b2FmK8YH+QEwq/Sy2uAEhmqL5nPfGYbJOcaqjeYYZoA= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= -sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= +sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= +sigs.k8s.io/controller-runtime v0.19.4/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= diff --git a/internal/service/bearer.go b/internal/authentication/bearer.go similarity index 66% rename from internal/service/bearer.go rename to internal/authentication/bearer.go index f1340def..eb276589 100644 --- a/internal/service/bearer.go +++ b/internal/authentication/bearer.go @@ -1,4 +1,4 @@ -package service +package authentication import ( "context" @@ -7,8 +7,28 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "k8s.io/apiserver/pkg/authentication/authenticator" ) +var _ = ContextAuthenticator(&BearerTokenAuthenticator{}) + +type BearerTokenAuthenticator struct { + auth authenticator.Token +} + +func NewBearerTokenAuthenticator(auth authenticator.Token) *BearerTokenAuthenticator { + return &BearerTokenAuthenticator{auth: auth} +} + +func (b *BearerTokenAuthenticator) AuthenticateContext(ctx context.Context) (*authenticator.Response, bool, error) { + token, err := BearerTokenFromContext(ctx) + if err != nil { + return nil, false, err + } + + return b.auth.AuthenticateToken(ctx, token) +} + func BearerTokenFromContext(ctx context.Context) (string, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { diff --git a/internal/authentication/types.go b/internal/authentication/types.go new file mode 100644 index 00000000..08632943 --- /dev/null +++ b/internal/authentication/types.go @@ -0,0 +1,11 @@ +package authentication + +import ( + "context" + + "k8s.io/apiserver/pkg/authentication/authenticator" +) + +type ContextAuthenticator interface { + AuthenticateContext(context.Context) (*authenticator.Response, bool, error) +} diff --git a/internal/authorization/basic.go b/internal/authorization/basic.go new file mode 100644 index 00000000..1d2f55c0 --- /dev/null +++ b/internal/authorization/basic.go @@ -0,0 +1,72 @@ +package authorization + +import ( + "context" + + jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "k8s.io/apiserver/pkg/authorization/authorizer" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type BasicAuthorizer struct { + reader client.Reader + prefix string +} + +func NewBasicAuthorizer(reader client.Reader, prefix string) *BasicAuthorizer { + return &BasicAuthorizer{reader: reader, prefix: prefix} +} + +func (b *BasicAuthorizer) Authorize( + ctx context.Context, + attributes authorizer.Attributes, +) (authorizer.Decision, string, error) { + switch attributes.GetResource() { + case "Exporter": + var e jumpstarterdevv1alpha1.Exporter + if err := b.reader.Get(ctx, client.ObjectKey{ + Namespace: attributes.GetNamespace(), + Name: attributes.GetName(), + }, &e); err != nil { + return authorizer.DecisionDeny, "failed to get exporter", err + } + if ExporterAuthorizedUsername(&e, b.prefix) == attributes.GetUser().GetName() { + return authorizer.DecisionAllow, "", nil + } else { + return authorizer.DecisionDeny, "", nil + } + case "Client": + var c jumpstarterdevv1alpha1.Client + if err := b.reader.Get(ctx, client.ObjectKey{ + Namespace: attributes.GetNamespace(), + Name: attributes.GetName(), + }, &c); err != nil { + return authorizer.DecisionDeny, "failed to get client", err + } + if ClientAuthorizedUsername(&c, b.prefix) == attributes.GetUser().GetName() { + return authorizer.DecisionAllow, "", nil + } else { + return authorizer.DecisionDeny, "", nil + } + default: + return authorizer.DecisionDeny, "invalid object kind", nil + } +} + +func ClientAuthorizedUsername(c *jumpstarterdevv1alpha1.Client, prefix string) string { + if c.Spec.Username == nil { + return prefix + "client:" + c.Namespace + ":" + c.Name + ":" + string(c.UID) + } else { + return *c.Spec.Username + } +} + +func ExporterAuthorizedUsername(e *jumpstarterdevv1alpha1.Exporter, prefix string) string { + if e.Spec.Username == nil { + return prefix + "exporter:" + e.Namespace + ":" + e.Name + ":" + string(e.UID) + } else { + return *e.Spec.Username + } +} + +var _ = authorizer.Authorizer(&BasicAuthorizer{}) diff --git a/internal/authorization/metadata.go b/internal/authorization/metadata.go new file mode 100644 index 00000000..3298a711 --- /dev/null +++ b/internal/authorization/metadata.go @@ -0,0 +1,72 @@ +package authorization + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +var _ = ContextAttributesGetter(&MetadataAttributesGetter{}) + +type MetadataAttributesGetterConfig struct { + NamespaceKey string + ResourceKey string + NameKey string +} + +type MetadataAttributesGetter struct { + config MetadataAttributesGetterConfig +} + +func NewMetadataAttributesGetter(config MetadataAttributesGetterConfig) *MetadataAttributesGetter { + return &MetadataAttributesGetter{ + config: config, + } +} + +func (b *MetadataAttributesGetter) ContextAttributes( + ctx context.Context, + userInfo user.Info, +) (authorizer.Attributes, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, status.Errorf(codes.InvalidArgument, "missing metadata") + } + + namespace, err := mdGet(md, b.config.NamespaceKey) + if err != nil { + return nil, err + } + + resource, err := mdGet(md, b.config.ResourceKey) + if err != nil { + return nil, err + } + + name, err := mdGet(md, b.config.NameKey) + if err != nil { + return nil, err + } + + return authorizer.AttributesRecord{ + User: userInfo, + Namespace: namespace, + Resource: resource, + Name: name, + }, nil +} + +func mdGet(md metadata.MD, k string) (string, error) { + v := md.Get(k) + if len(v) < 1 { + return "", status.Errorf(codes.InvalidArgument, "missing metadata: %s", k) + } + if len(v) > 1 { + return "", status.Errorf(codes.InvalidArgument, "multiple metadata: %s", k) + } + return v[0], nil +} diff --git a/internal/authorization/types.go b/internal/authorization/types.go new file mode 100644 index 00000000..7ee05a58 --- /dev/null +++ b/internal/authorization/types.go @@ -0,0 +1,12 @@ +package authorization + +import ( + "context" + + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +type ContextAttributesGetter interface { + ContextAttributes(context.Context, user.Info) (authorizer.Attributes, error) +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 00000000..f16255c0 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,44 @@ +package config + +import ( + "context" + "fmt" + + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/authentication/authenticator" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func LoadConfiguration( + ctx context.Context, + client client.Reader, + scheme *runtime.Scheme, + key client.ObjectKey, + signer *oidc.Signer, + certificateAuthority string, +) (authenticator.Token, error) { + var configmap corev1.ConfigMap + if err := client.Get(ctx, key, &configmap); err != nil { + return nil, err + } + + rawAuthenticationConfiguration, ok := configmap.Data["authentication"] + if !ok { + return nil, fmt.Errorf("LoadConfiguration: missing authentication section") + } + + authenticator, err := oidc.LoadAuthenticationConfiguration( + ctx, + scheme, + []byte(rawAuthenticationConfiguration), + signer, + certificateAuthority, + ) + if err != nil { + return nil, err + } + + return authenticator, nil +} diff --git a/internal/controller/client_controller.go b/internal/controller/client_controller.go index 59e79404..cfee266f 100644 --- a/internal/controller/client_controller.go +++ b/internal/controller/client_controller.go @@ -30,12 +30,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authorization" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" ) // ClientReconciler reconciles a Client object type ClientReconciler struct { client.Client Scheme *runtime.Scheme + Signer *oidc.Signer } // +kubebuilder:rbac:groups=jumpstarter.dev,resources=clients,verbs=get;list;watch;create;update;patch;delete @@ -111,12 +114,7 @@ func (r *ClientReconciler) reconcileStatusEndpoint( } func (r *ClientReconciler) secretForClient(client *jumpstarterdevv1alpha1.Client) (*corev1.Secret, error) { - token, err := SignObjectToken( - "https://jumpstarter.dev/controller", - []string{"https://jumpstarter.dev/controller"}, - client, - r.Scheme, - ) + token, err := r.Signer.Token(authorization.ClientAuthorizedUsername(client, r.Signer.Prefix())) if err != nil { return nil, err } diff --git a/internal/controller/client_controller_test.go b/internal/controller/client_controller_test.go index bbfd741e..49520679 100644 --- a/internal/controller/client_controller_test.go +++ b/internal/controller/client_controller_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" ) var _ = Describe("Identity Controller", func() { @@ -78,12 +79,16 @@ 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:") + Expect(err).NotTo(HaveOccurred()) + controllerReconciler := &ClientReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), + Signer: signer, } - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, }) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/exporter_controller.go b/internal/controller/exporter_controller.go index a16fc11b..aa0c257e 100644 --- a/internal/controller/exporter_controller.go +++ b/internal/controller/exporter_controller.go @@ -29,17 +29,21 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authorization" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" ) // ExporterReconciler reconciles a Exporter object type ExporterReconciler struct { client.Client Scheme *runtime.Scheme + Signer *oidc.Signer } // +kubebuilder:rbac:groups=jumpstarter.dev,resources=exporters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=jumpstarter.dev,resources=exporters/status,verbs=get;update;patch // +kubebuilder:rbac:groups=jumpstarter.dev,resources=exporters/finalizers,verbs=update +// +kubebuilder:rbac:groups=jumpstarter.dev,resources=exporteraccesspolicies,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;delete // Reconcile is part of the main kubernetes reconciliation loop which aims to @@ -150,12 +154,7 @@ func (r *ExporterReconciler) reconcileStatusEndpoint( } func (r *ExporterReconciler) secretForExporter(exporter *jumpstarterdevv1alpha1.Exporter) (*corev1.Secret, error) { - token, err := SignObjectToken( - "https://jumpstarter.dev/controller", - []string{"https://jumpstarter.dev/controller"}, - exporter, - r.Scheme, - ) + token, err := r.Signer.Token(authorization.ExporterAuthorizedUsername(exporter, r.Signer.Prefix())) if err != nil { return nil, err } diff --git a/internal/controller/exporter_controller_test.go b/internal/controller/exporter_controller_test.go index 45cd67b6..ed9f0fe0 100644 --- a/internal/controller/exporter_controller_test.go +++ b/internal/controller/exporter_controller_test.go @@ -29,6 +29,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" ) var _ = Describe("Exporter Controller", func() { @@ -78,12 +79,16 @@ 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:") + Expect(err).NotTo(HaveOccurred()) + controllerReconciler := &ExporterReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), + Signer: signer, } - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, }) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/lease_controller.go b/internal/controller/lease_controller.go index 48b12707..83eb5fc3 100644 --- a/internal/controller/lease_controller.go +++ b/internal/controller/lease_controller.go @@ -26,6 +26,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -267,7 +268,75 @@ func (r *LeaseReconciler) reconcileStatusExporterRef( return false }) - if len(availableExporters) == 0 { + var approvedExporters []struct { + Exporter jumpstarterdevv1alpha1.Exporter + Policy jumpstarterdevv1alpha1.Policy + } + + var policies jumpstarterdevv1alpha1.ExporterAccessPolicyList + if err := r.List(ctx, &policies, + client.InNamespace(lease.Namespace), + ); err != nil { + return fmt.Errorf("reconcileStatusExporterRef: failed to list exporter access policies: %w", err) + } + + if len(policies.Items) == 0 { + for _, exporter := range availableExporters { + approvedExporters = append(approvedExporters, struct { + Exporter jumpstarterdevv1alpha1.Exporter + Policy jumpstarterdevv1alpha1.Policy + }{ + Exporter: exporter, + Policy: jumpstarterdevv1alpha1.Policy{ + Priority: 0, + SpotAccess: false, + }, + }) + } + } else { + var jclient jumpstarterdevv1alpha1.Client + if err := r.Get(ctx, types.NamespacedName{ + Namespace: lease.Namespace, + Name: lease.Spec.ClientRef.Name, + }, &jclient); err != nil { + return fmt.Errorf("reconcileStatusExporterRef: failed to get client: %w", err) + } + + for _, exporter := range availableExporters { + for _, policy := range policies.Items { + exporterSelector, err := metav1.LabelSelectorAsSelector(&policy.Spec.ExporterSelector) + if err != nil { + return fmt.Errorf("reconcileStatusExporterRef: failed to convert exporter selector: %w", err) + } + if exporterSelector.Matches(labels.Set(exporter.Labels)) { + for _, p := range policy.Spec.Policies { + for _, from := range p.From { + clientSelector, err := metav1.LabelSelectorAsSelector(&from.ClientSelector) + if err != nil { + return fmt.Errorf("reconcileStatusExporterRef: failed to convert client selector: %w", err) + } + if clientSelector.Matches(labels.Set(jclient.Labels)) { + if p.MaximumDuration != nil { + if lease.Spec.Duration.Duration > p.MaximumDuration.Duration { + continue + } + } + approvedExporters = append(approvedExporters, struct { + Exporter jumpstarterdevv1alpha1.Exporter + Policy jumpstarterdevv1alpha1.Policy + }{ + Exporter: exporter, + Policy: p, + }) + } + } + } + } + } + } + } + + if len(approvedExporters) == 0 { meta.SetStatusCondition(&lease.Status.Conditions, metav1.Condition{ Type: string(jumpstarterdevv1alpha1.LeaseConditionTypePending), Status: metav1.ConditionTrue, @@ -279,12 +348,15 @@ func (r *LeaseReconciler) reconcileStatusExporterRef( }) result.RequeueAfter = time.Second return nil - } else { - lease.Status.ExporterRef = &corev1.LocalObjectReference{ - Name: availableExporters[0].Name, - } - return nil } + + selected := approvedExporters[0] + lease.Status.Priority = selected.Policy.Priority + lease.Status.SpotAccess = selected.Policy.SpotAccess + lease.Status.ExporterRef = &corev1.LocalObjectReference{ + Name: selected.Exporter.Name, + } + return nil } return nil diff --git a/internal/controller/lease_controller_test.go b/internal/controller/lease_controller_test.go index 73d97418..1f4f7d0d 100644 --- a/internal/controller/lease_controller_test.go +++ b/internal/controller/lease_controller_test.go @@ -21,6 +21,7 @@ import ( "time" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -335,9 +336,13 @@ func reconcileLease(ctx context.Context, lease *jumpstarterdevv1alpha1.Lease) re Scheme: k8sClient.Scheme(), } + signer, err := oidc.NewSignerFromSeed([]byte{}, "https://example.com", "dummy", "dummy:") + Expect(err).NotTo(HaveOccurred()) + exporterReconciler := &ExporterReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), + Signer: signer, } res, err := leaseReconciler.Reconcile(ctx, reconcile.Request{ diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 04e2afdf..22a2e32d 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -39,6 +39,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" // +kubebuilder:scaffold:imports ) @@ -121,12 +122,16 @@ 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:") + Expect(err).NotTo(HaveOccurred()) + controllerReconciler := &ExporterReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), + Signer: signer, } - _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + _, err = controllerReconciler.Reconcile(ctx, reconcile.Request{ NamespacedName: typeNamespacedName, }) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/token.go b/internal/controller/token.go deleted file mode 100644 index 5263805b..00000000 --- a/internal/controller/token.go +++ /dev/null @@ -1,124 +0,0 @@ -package controller - -import ( - "context" - "fmt" - "os" - "time" - - "github.com/golang-jwt/jwt/v5" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/uuid" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" -) - -type JumpstarterClaims struct { - jwt.RegisteredClaims - // corev1.ObjectReference - Kind string `json:"kubernetes.io/kind,omitempty"` - Namespace string `json:"kubernetes.io/namespace,omitempty"` - Name string `json:"kubernetes.io/name,omitempty"` - UID types.UID `json:"kubernetes.io/uid,omitempty"` - APIVersion string `json:"kubernetes.io/api_version,omitempty"` -} - -func KeyFunc(_ *jwt.Token) (interface{}, error) { - key, ok := os.LookupEnv("CONTROLLER_KEY") - if !ok { - return nil, fmt.Errorf("Failed to lookup controller key from env") - } - return []byte(key), nil -} - -func SignObjectToken( - issuer string, - audience []string, - object metav1.Object, - scheme *runtime.Scheme, -) (string, error) { - ro, ok := object.(runtime.Object) - if !ok { - return "", fmt.Errorf("%T is not a runtime.Object, cannot call SignObjectToken", object) - } - - gvk, err := apiutil.GVKForObject(ro, scheme) - if err != nil { - return "", err - } - - key, err := KeyFunc(nil) - if err != nil { - return "", err - } - - return jwt.NewWithClaims(jwt.SigningMethodHS256, JumpstarterClaims{ - RegisteredClaims: jwt.RegisteredClaims{ - Issuer: issuer, - Subject: string(object.GetUID()), - Audience: audience, - // ExpiresAt: token are valid for the entire lifetime of the object - NotBefore: jwt.NewNumericDate(time.Now()), - IssuedAt: jwt.NewNumericDate(time.Now()), - ID: string(uuid.NewUUID()), - }, - Kind: gvk.Kind, - Namespace: object.GetNamespace(), - Name: object.GetName(), - UID: object.GetUID(), - APIVersion: gvk.GroupVersion().String(), - }).SignedString(key) -} - -type Object[T any] interface { - client.Object - *T -} - -func VerifyObjectToken[T any, PT Object[T]]( - ctx context.Context, - token string, - issuer string, - audience string, - client client.Client, -) (*T, error) { - parsed, err := jwt.ParseWithClaims( - token, - &JumpstarterClaims{}, - KeyFunc, - jwt.WithIssuer(issuer), - jwt.WithAudience(audience), - jwt.WithIssuedAt(), - jwt.WithValidMethods([]string{ - jwt.SigningMethodHS256.Name, - jwt.SigningMethodHS384.Name, - jwt.SigningMethodHS512.Name, - }), - ) - if err != nil { - return nil, err - } else if claims, ok := parsed.Claims.(*JumpstarterClaims); ok { - var object T - err = client.Get( - ctx, - types.NamespacedName{ - Namespace: claims.Namespace, - Name: claims.Name, - }, - PT(&object), - ) - if err != nil { - return nil, err - } - - if PT(&object).GetUID() != claims.UID { - return nil, fmt.Errorf("VerifyObjectToken: UID mismatch") - } - - return &object, nil - } else { - return nil, fmt.Errorf("%T is not a JumpstarterClaims", parsed.Claims) - } -} diff --git a/internal/oidc/config.go b/internal/oidc/config.go new file mode 100644 index 00000000..f0c53b09 --- /dev/null +++ b/internal/oidc/config.go @@ -0,0 +1,90 @@ +package oidc + +import ( + "context" + + jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apiserver/pkg/apis/apiserver" + apiserverv1beta1 "k8s.io/apiserver/pkg/apis/apiserver/v1beta1" + "k8s.io/apiserver/pkg/authentication/authenticator" + 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( + ctx context.Context, + scheme *runtime.Scheme, + configuration []byte, + signer *Signer, + certificateAuthority string, +) (authenticator.Token, error) { + var authenticationConfiguration jumpstarterdevv1alpha1.AuthenticationConfiguration + if err := runtime.DecodeInto( + serializer.NewCodecFactory(scheme, serializer.EnableStrict). + UniversalDecoder(jumpstarterdevv1alpha1.GroupVersion), + configuration, + &authenticationConfiguration, + ); err != nil { + return nil, err + } + + authenticationConfiguration.JWT = append(authenticationConfiguration.JWT, apiserverv1beta1.JWTAuthenticator{ + Issuer: apiserverv1beta1.Issuer{ + URL: signer.Issuer(), + CertificateAuthority: certificateAuthority, + Audiences: []string{signer.Audience()}, + }, + ClaimMappings: apiserverv1beta1.ClaimMappings{ + Username: apiserverv1beta1.PrefixedClaimOrExpression{ + Claim: "sub", + Prefix: ptr.To(signer.Prefix()), + }, + }, + }) + + return newJWTAuthenticator( + ctx, + scheme, + authenticationConfiguration, + ) +} + +// Reference: https://github.com/kubernetes/kubernetes/blob/v1.32.1/pkg/kubeapiserver/authenticator/config.go#L244 +func newJWTAuthenticator( + ctx context.Context, + scheme *runtime.Scheme, + config jumpstarterdevv1alpha1.AuthenticationConfiguration, +) (authenticator.Token, error) { + var jwtAuthenticators []authenticator.Token + for _, jwtAuthenticator := range config.JWT { + var oidcCAContent oidc.CAContentProvider + if len(jwtAuthenticator.Issuer.CertificateAuthority) > 0 { + var oidcCAError error + oidcCAContent, oidcCAError = dynamiccertificates.NewStaticCAContent( + "oidc-authenticator", + []byte(jwtAuthenticator.Issuer.CertificateAuthority), + ) + if oidcCAError != nil { + return nil, oidcCAError + } + } + var jwtAuthenticatorUnversioned apiserver.JWTAuthenticator + if err := scheme.Convert(&jwtAuthenticator, &jwtAuthenticatorUnversioned, nil); err != nil { + return nil, err + } + oidcAuth, err := oidc.New(ctx, oidc.Options{ + JWTAuthenticator: jwtAuthenticatorUnversioned, + CAContentProvider: oidcCAContent, + SupportedSigningAlgs: oidc.AllValidSigningAlgorithms(), + }) + if err != nil { + return nil, err + } + jwtAuthenticators = append(jwtAuthenticators, oidcAuth) + } + return tokenunion.NewFailOnError(jwtAuthenticators...), nil +} diff --git a/internal/oidc/op.go b/internal/oidc/op.go new file mode 100644 index 00000000..6c86e0b7 --- /dev/null +++ b/internal/oidc/op.go @@ -0,0 +1,106 @@ +package oidc + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "encoding/binary" + "math/rand" + "strings" + "time" + + "filippo.io/keygen" + "github.com/gin-gonic/gin" + "github.com/go-jose/go-jose/v4" + "github.com/golang-jwt/jwt/v5" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" +) + +type Signer struct { + privatekey *ecdsa.PrivateKey + issuer string + audience string + prefix string +} + +func NewSigner(privateKey *ecdsa.PrivateKey, issuer, audience, prefix string) *Signer { + return &Signer{ + privatekey: privateKey, + issuer: issuer, + audience: audience, + prefix: prefix, + } +} + +func NewSignerFromSeed(seed []byte, issuer, audience, prefix string) (*Signer, error) { + hash := sha256.Sum256(seed) + source := rand.NewSource(int64(binary.BigEndian.Uint64(hash[:8]))) + reader := rand.New(source) + key, err := keygen.ECDSALegacy(elliptic.P256(), reader) + if err != nil { + return nil, err + } + return NewSigner(key, issuer, audience, prefix), nil +} + +func (k *Signer) Issuer() string { + return k.issuer +} + +func (k *Signer) Audience() string { + return k.audience +} + +func (k *Signer) Prefix() string { + return k.prefix +} + +func (k *Signer) ID() string { + return "default" +} + +func (k *Signer) Algorithm() jose.SignatureAlgorithm { + return jose.ES256 +} + +func (k *Signer) Use() string { + return "sig" +} + +func (k *Signer) Key() any { + return k.privatekey.Public() +} + +func (k *Signer) KeySet(context.Context) ([]op.Key, error) { + return []op.Key{k}, nil +} + +func (k *Signer) Register(group gin.IRoutes) { + group.GET("/.well-known/openid-configuration", func(c *gin.Context) { + op.Discover(c.Writer, &oidc.DiscoveryConfiguration{ + Issuer: k.issuer, + JwksURI: k.issuer + "/jwks", + }) + }) + + group.GET("/jwks", func(c *gin.Context) { + op.Keys(c.Writer, c.Request, k) + }) +} + +func (k *Signer) Token( + subject string, +) (string, error) { + if !strings.HasPrefix(subject, k.prefix) { + return "placeholder for external OIDC provider access token", nil + } + return jwt.NewWithClaims(jwt.SigningMethodES256, jwt.RegisteredClaims{ + Issuer: k.issuer, + Subject: strings.TrimPrefix(subject, k.prefix), + Audience: []string{k.audience}, + IssuedAt: jwt.NewNumericDate(time.Now()), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(365 * 24 * time.Hour)), // FIXME: rotate keys on expiration + }).SignedString(k.privatekey) +} diff --git a/internal/oidc/token.go b/internal/oidc/token.go new file mode 100644 index 00000000..4030bd1b --- /dev/null +++ b/internal/oidc/token.go @@ -0,0 +1,105 @@ +package oidc + +import ( + "context" + "fmt" + + jumpstarterdevv1alpha1 "github.com/jumpstarter-dev/jumpstarter-controller/api/v1alpha1" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authentication" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authorization" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "k8s.io/apimachinery/pkg/types" + "k8s.io/apiserver/pkg/authorization/authorizer" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func VerifyOIDCToken( + ctx context.Context, + auth authentication.ContextAuthenticator, + attr authorization.ContextAttributesGetter, +) (authorizer.Attributes, error) { + resp, ok, err := auth.AuthenticateContext(ctx) + if err != nil { + return nil, err + } + + if !ok { + return nil, fmt.Errorf("failed to authenticate token") + } + + return attr.ContextAttributes(ctx, resp.User) +} + +func VerifyClientObjectToken( + ctx context.Context, + authz authentication.ContextAuthenticator, + authn authorizer.Authorizer, + attr authorization.ContextAttributesGetter, + kclient client.Client, +) (*jumpstarterdevv1alpha1.Client, error) { + attrs, err := VerifyOIDCToken(ctx, authz, attr) + if err != nil { + return nil, err + } + + if attrs.GetResource() != "Client" { + return nil, status.Errorf(codes.InvalidArgument, "object kind mismatch") + } + + decision, _, err := authn.Authorize(ctx, attrs) + if err != nil { + return nil, err + } + + if decision != authorizer.DecisionAllow { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + var client jumpstarterdevv1alpha1.Client + if err = kclient.Get(ctx, types.NamespacedName{ + Namespace: attrs.GetNamespace(), + Name: attrs.GetName(), + }, &client); err != nil { + return nil, err + } + + return &client, nil +} + +func VerifyExporterObjectToken( + ctx context.Context, + authz authentication.ContextAuthenticator, + authn authorizer.Authorizer, + attr authorization.ContextAttributesGetter, + kclient client.Client, +) (*jumpstarterdevv1alpha1.Exporter, error) { + attrs, err := VerifyOIDCToken(ctx, authz, attr) + if err != nil { + return nil, err + } + + if attrs.GetResource() != "Exporter" { + return nil, status.Errorf(codes.InvalidArgument, "object kind mismatch") + } + + decision, _, err := authn.Authorize(ctx, attrs) + if err != nil { + return nil, err + } + + if decision != authorizer.DecisionAllow { + return nil, status.Errorf(codes.PermissionDenied, "permission denied") + } + + var exporter jumpstarterdevv1alpha1.Exporter + if err = kclient.Get(ctx, types.NamespacedName{ + Namespace: attrs.GetNamespace(), + Name: attrs.GetName(), + }, &exporter); err != nil { + return nil, err + } + + return &exporter, nil +} diff --git a/internal/service/controller_service.go b/internal/service/controller_service.go index dfcb5d55..59512b70 100644 --- a/internal/service/controller_service.go +++ b/internal/service/controller_service.go @@ -26,6 +26,9 @@ import ( "time" "github.com/golang-jwt/jwt/v5" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authentication" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authorization" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" pb "github.com/jumpstarter-dev/jumpstarter-controller/internal/protocol/jumpstarter/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -44,6 +47,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/watch" + "k8s.io/apiserver/pkg/authorization/authorizer" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -57,35 +61,28 @@ type ControllerService struct { pb.UnimplementedControllerServiceServer Client client.WithWatch Scheme *runtime.Scheme + Authn authentication.ContextAuthenticator + Authz authorizer.Authorizer + Attr authorization.ContextAttributesGetter listenQueues sync.Map } func (s *ControllerService) authenticateClient(ctx context.Context) (*jumpstarterdevv1alpha1.Client, error) { - token, err := BearerTokenFromContext(ctx) - if err != nil { - return nil, err - } - - return controller.VerifyObjectToken[jumpstarterdevv1alpha1.Client]( + return oidc.VerifyClientObjectToken( ctx, - token, - "https://jumpstarter.dev/controller", - "https://jumpstarter.dev/controller", + s.Authn, + s.Authz, + s.Attr, s.Client, ) } func (s *ControllerService) authenticateExporter(ctx context.Context) (*jumpstarterdevv1alpha1.Exporter, error) { - token, err := BearerTokenFromContext(ctx) - if err != nil { - return nil, err - } - - return controller.VerifyObjectToken[jumpstarterdevv1alpha1.Exporter]( + return oidc.VerifyExporterObjectToken( ctx, - token, - "https://jumpstarter.dev/controller", - "https://jumpstarter.dev/controller", + s.Authn, + s.Authz, + s.Attr, s.Client, ) } @@ -207,10 +204,12 @@ func (s *ControllerService) ListExporters( ctx context.Context, req *pb.ListExportersRequest, ) (*pb.ListExportersResponse, error) { - // FIXME: authenticate client - logger := log.FromContext(ctx) + if _, err := s.authenticateClient(ctx); err != nil { + return nil, err + } + var exporters jumpstarterdevv1alpha1.ExporterList selector := labels.Everything() diff --git a/internal/service/oidc_service.go b/internal/service/oidc_service.go new file mode 100644 index 00000000..d3038ad6 --- /dev/null +++ b/internal/service/oidc_service.go @@ -0,0 +1,39 @@ +package service + +import ( + "context" + "crypto/tls" + "net" + + "github.com/gin-gonic/gin" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc" + ctrl "sigs.k8s.io/controller-runtime" +) + +// RouterService exposes a gRPC service +type OIDCService struct { + Signer *oidc.Signer + Cert *tls.Certificate +} + +func (s *OIDCService) Start(ctx context.Context) error { + r := gin.Default() + + s.Signer.Register(r) + + lis, err := net.Listen("tcp", "127.0.0.1:8085") + if err != nil { + return err + } + + tlslis := tls.NewListener(lis, &tls.Config{ + Certificates: []tls.Certificate{*s.Cert}, + }) + + return r.RunListener(tlslis) +} + +// SetupWithManager sets up the controller with the Manager. +func (s *OIDCService) SetupWithManager(mgr ctrl.Manager) error { + return mgr.Add(s) +} diff --git a/internal/service/router_service.go b/internal/service/router_service.go index 74b06fcc..560bc379 100644 --- a/internal/service/router_service.go +++ b/internal/service/router_service.go @@ -23,6 +23,7 @@ import ( "sync" "github.com/golang-jwt/jwt/v5" + "github.com/jumpstarter-dev/jumpstarter-controller/internal/authentication" pb "github.com/jumpstarter-dev/jumpstarter-controller/internal/protocol/jumpstarter/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -49,7 +50,7 @@ type streamContext struct { } func (s *RouterService) authenticate(ctx context.Context) (string, error) { - token, err := BearerTokenFromContext(ctx) + token, err := authentication.BearerTokenFromContext(ctx) if err != nil { return "", err } From 48775b491c2d383f556858994f859d13fc806c62 Mon Sep 17 00:00:00 2001 From: Nick Cao Date: Tue, 11 Feb 2025 10:18:20 -0500 Subject: [PATCH 2/2] Fix typo in main.go --- cmd/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index b8fa155a..bfe1c8b0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -229,7 +229,7 @@ func main() { Signer: oidcSigner, Cert: oidcCert, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create service", "service", "Dashboard") + setupLog.Error(err, "unable to create service", "service", "OIDC") os.Exit(1) }