From fc95b4da361e2e59ac37d19fd621a42080e519e7 Mon Sep 17 00:00:00 2001 From: huabing zhao Date: Fri, 23 Feb 2024 14:22:28 +0800 Subject: [PATCH 1/3] load client secret from k8s Secret when reading config Signed-off-by: huabing zhao --- config/gen/go/v1/config.pb.go | 10 +- config/gen/go/v1/mock/config.pb.go | 5 +- config/gen/go/v1/oidc/config.pb.go | 329 ++++++++++++------ config/gen/go/v1/oidc/config.pb.validate.go | 196 ++++++++++- config/v1/oidc/config.proto | 29 +- go.mod | 7 +- go.sum | 29 +- internal/authz/oidc_test.go | 12 +- internal/config.go | 93 +++++ internal/config_test.go | 98 +++++- .../oidc-with-invalid-secret-ref.json | 38 ++ .../oidc-with-non-existing-secret-ref.json | 38 ++ .../testdata/oidc-with-valid-secret-ref.json | 38 ++ 13 files changed, 781 insertions(+), 141 deletions(-) create mode 100644 internal/testdata/oidc-with-invalid-secret-ref.json create mode 100644 internal/testdata/oidc-with-non-existing-secret-ref.json create mode 100644 internal/testdata/oidc-with-valid-secret-ref.json diff --git a/config/gen/go/v1/config.pb.go b/config/gen/go/v1/config.pb.go index 2417fee..ce89240 100644 --- a/config/gen/go/v1/config.pb.go +++ b/config/gen/go/v1/config.pb.go @@ -21,15 +21,13 @@ package configv1 import ( - reflect "reflect" - sync "sync" - _ "github.com/envoyproxy/protoc-gen-validate/validate" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - mock "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" oidc "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( diff --git a/config/gen/go/v1/mock/config.pb.go b/config/gen/go/v1/mock/config.pb.go index c8b9a8b..5b934e3 100644 --- a/config/gen/go/v1/mock/config.pb.go +++ b/config/gen/go/v1/mock/config.pb.go @@ -21,11 +21,10 @@ package mock import ( - reflect "reflect" - sync "sync" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) const ( diff --git a/config/gen/go/v1/oidc/config.pb.go b/config/gen/go/v1/oidc/config.pb.go index 6ca75fc..e564656 100644 --- a/config/gen/go/v1/oidc/config.pb.go +++ b/config/gen/go/v1/oidc/config.pb.go @@ -21,13 +21,12 @@ package oidc import ( - reflect "reflect" - sync "sync" - _ "github.com/envoyproxy/protoc-gen-validate/validate" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" + reflect "reflect" + sync "sync" ) const ( @@ -258,10 +257,11 @@ type OIDCConfig struct { // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). // Required. ClientId string `protobuf:"bytes,5,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` - // The OIDC client secret assigned to the filter to be used in the - // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). - // Required. - ClientSecret string `protobuf:"bytes,6,opt,name=client_secret,json=clientSecret,proto3" json:"client_secret,omitempty"` + // Types that are assignable to ClientSecretConfig: + // + // *OIDCConfig_ClientSecret + // *OIDCConfig_ClientSecretRef + ClientSecretConfig isOIDCConfig_ClientSecretConfig `protobuf_oneof:"client_secret_config"` // Additional scopes passed to the OIDC Provider in the // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). // The `openid` scope is always sent to the OIDC Provider, and does not need to be specified here. @@ -430,13 +430,27 @@ func (x *OIDCConfig) GetClientId() string { return "" } +func (m *OIDCConfig) GetClientSecretConfig() isOIDCConfig_ClientSecretConfig { + if m != nil { + return m.ClientSecretConfig + } + return nil +} + func (x *OIDCConfig) GetClientSecret() string { - if x != nil { + if x, ok := x.GetClientSecretConfig().(*OIDCConfig_ClientSecret); ok { return x.ClientSecret } return "" } +func (x *OIDCConfig) GetClientSecretRef() *OIDCConfig_SecretReference { + if x, ok := x.GetClientSecretConfig().(*OIDCConfig_ClientSecretRef); ok { + return x.ClientSecretRef + } + return nil +} + func (x *OIDCConfig) GetScopes() []string { if x != nil { return x.Scopes @@ -551,6 +565,31 @@ func (*OIDCConfig_Jwks) isOIDCConfig_JwksConfig() {} func (*OIDCConfig_JwksFetcher) isOIDCConfig_JwksConfig() {} +type isOIDCConfig_ClientSecretConfig interface { + isOIDCConfig_ClientSecretConfig() +} + +type OIDCConfig_ClientSecret struct { + // The OIDC client secret assigned to the filter to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // This field keeps the client secret in plain text. Recommend to use `client_secret_ref` instead + // when running in a Kubernetes cluster. + ClientSecret string `protobuf:"bytes,6,opt,name=client_secret,json=clientSecret,proto3,oneof"` +} + +type OIDCConfig_ClientSecretRef struct { + // The Kubernetes secret that contains the OIDC client secret assigned to the filter to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // This is an Opaque secret. The client secret should be stored in the key "client-secret". + // This filed is only valid when running in a Kubernetes cluster. + ClientSecretRef *OIDCConfig_SecretReference `protobuf:"bytes,21,opt,name=client_secret_ref,json=clientSecretRef,proto3,oneof"` +} + +func (*OIDCConfig_ClientSecret) isOIDCConfig_ClientSecretConfig() {} + +func (*OIDCConfig_ClientSecretRef) isOIDCConfig_ClientSecretConfig() {} + type isOIDCConfig_TrustedCaConfig interface { isOIDCConfig_TrustedCaConfig() } @@ -650,6 +689,64 @@ func (x *OIDCConfig_JwksFetcherConfig) GetSkipVerifyPeerCert() *structpb.Value { return nil } +// This message defines a reference to a Kubernetes Secret resource. +type OIDCConfig_SecretReference struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The namespace of the referenced Secret, if not set, default to "default" namespace. + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + // The name of the referenced Secret. + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *OIDCConfig_SecretReference) Reset() { + *x = OIDCConfig_SecretReference{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_oidc_config_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OIDCConfig_SecretReference) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OIDCConfig_SecretReference) ProtoMessage() {} + +func (x *OIDCConfig_SecretReference) ProtoReflect() protoreflect.Message { + mi := &file_v1_oidc_config_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OIDCConfig_SecretReference.ProtoReflect.Descriptor instead. +func (*OIDCConfig_SecretReference) Descriptor() ([]byte, []int) { + return file_v1_oidc_config_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *OIDCConfig_SecretReference) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *OIDCConfig_SecretReference) GetName() string { + if x != nil { + return x.Name + } + return "" +} + var File_v1_oidc_config_proto protoreflect.FileDescriptor var file_v1_oidc_config_proto_rawDesc = []byte{ @@ -673,7 +770,7 @@ var file_v1_oidc_config_proto_rawDesc = []byte{ 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2a, 0x0a, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x55, 0x72, 0x69, 0x22, 0xcc, 0x0a, 0x0a, 0x0a, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, + 0x63, 0x74, 0x55, 0x72, 0x69, 0x22, 0x96, 0x0c, 0x0a, 0x0a, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, @@ -694,87 +791,99 @@ var file_v1_oidc_config_proto_rawDesc = []byte{ 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6f, 0x6b, - 0x69, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x4c, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, - 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x69, 0x64, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, - 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x28, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4c, 0x6f, - 0x67, 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x6f, - 0x75, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, - 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x44, - 0x0a, 0x1d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x1b, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x22, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, - 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x01, 0x52, 0x1f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x75, 0x72, 0x69, - 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x72, 0x69, - 0x12, 0x64, 0x0a, 0x1a, 0x72, 0x65, 0x64, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0c, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x64, 0x0a, 0x11, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x15, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, - 0x63, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x17, 0x72, - 0x65, 0x64, 0x69, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x6f, 0x72, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, - 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, - 0x74, 0x1a, 0xbc, 0x01, 0x0a, 0x11, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, - 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, - 0x72, 0x69, 0x12, 0x3d, 0x0a, 0x1b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x5f, 0x66, - 0x65, 0x74, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, - 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, - 0x63, 0x46, 0x65, 0x74, 0x63, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, - 0x63, 0x12, 0x4d, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, - 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x73, 0x6b, + 0x63, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x01, 0x52, 0x0f, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x4c, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, + 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x69, 0x64, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, + 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, + 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x40, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x28, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4c, 0x6f, 0x67, + 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, + 0x74, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x16, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x69, + 0x64, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x44, 0x0a, + 0x1d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x1b, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x22, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x02, 0x52, 0x1f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x69, + 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x72, 0x69, 0x12, + 0x64, 0x0a, 0x1a, 0x72, 0x65, 0x64, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, + 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x17, 0x72, 0x65, + 0x64, 0x69, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, - 0x42, 0x0d, 0x0a, 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, - 0x13, 0x0a, 0x11, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x42, 0xf4, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, - 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x42, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x74, 0x65, 0x74, 0x72, 0x61, 0x74, 0x65, 0x69, 0x6f, 0x2f, 0x61, 0x75, 0x74, - 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x69, 0x64, - 0x63, 0xa2, 0x02, 0x04, 0x41, 0x43, 0x56, 0x4f, 0xaa, 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x56, 0x31, - 0x2e, 0x4f, 0x69, 0x64, 0x63, 0xca, 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, - 0x64, 0x63, 0xe2, 0x02, 0x26, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0x5c, - 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1d, 0x41, 0x75, - 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x3a, 0x3a, 0x4f, 0x69, 0x64, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x1a, 0xbc, 0x01, 0x0a, 0x11, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, + 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, 0x72, + 0x69, 0x12, 0x3d, 0x0a, 0x1b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x5f, 0x66, 0x65, + 0x74, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, + 0x46, 0x65, 0x74, 0x63, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, + 0x12, 0x4d, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, + 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x73, 0x6b, 0x69, + 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x1a, + 0x4c, 0x0a, 0x0f, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, + 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x1b, 0x0a, 0x14, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0xf4, + 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, + 0x63, 0x42, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x65, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x69, 0x6f, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2d, 0x67, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x65, 0x6e, + 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0xa2, 0x02, 0x04, 0x41, 0x43, + 0x56, 0x4f, 0xaa, 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x56, 0x31, 0x2e, 0x4f, 0x69, 0x64, 0x63, 0xca, + 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0xe2, 0x02, 0x26, 0x41, + 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1d, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x3a, + 0x3a, 0x4f, 0x69, 0x64, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -789,28 +898,30 @@ func file_v1_oidc_config_proto_rawDescGZIP() []byte { return file_v1_oidc_config_proto_rawDescData } -var file_v1_oidc_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_v1_oidc_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_v1_oidc_config_proto_goTypes = []interface{}{ (*TokenConfig)(nil), // 0: authservice.config.v1.oidc.TokenConfig (*RedisConfig)(nil), // 1: authservice.config.v1.oidc.RedisConfig (*LogoutConfig)(nil), // 2: authservice.config.v1.oidc.LogoutConfig (*OIDCConfig)(nil), // 3: authservice.config.v1.oidc.OIDCConfig (*OIDCConfig_JwksFetcherConfig)(nil), // 4: authservice.config.v1.oidc.OIDCConfig.JwksFetcherConfig - (*structpb.Value)(nil), // 5: google.protobuf.Value + (*OIDCConfig_SecretReference)(nil), // 5: authservice.config.v1.oidc.OIDCConfig.SecretReference + (*structpb.Value)(nil), // 6: google.protobuf.Value } var file_v1_oidc_config_proto_depIdxs = []int32{ 4, // 0: authservice.config.v1.oidc.OIDCConfig.jwks_fetcher:type_name -> authservice.config.v1.oidc.OIDCConfig.JwksFetcherConfig - 0, // 1: authservice.config.v1.oidc.OIDCConfig.id_token:type_name -> authservice.config.v1.oidc.TokenConfig - 0, // 2: authservice.config.v1.oidc.OIDCConfig.access_token:type_name -> authservice.config.v1.oidc.TokenConfig - 2, // 3: authservice.config.v1.oidc.OIDCConfig.logout:type_name -> authservice.config.v1.oidc.LogoutConfig - 1, // 4: authservice.config.v1.oidc.OIDCConfig.redis_session_store_config:type_name -> authservice.config.v1.oidc.RedisConfig - 5, // 5: authservice.config.v1.oidc.OIDCConfig.skip_verify_peer_cert:type_name -> google.protobuf.Value - 5, // 6: authservice.config.v1.oidc.OIDCConfig.JwksFetcherConfig.skip_verify_peer_cert:type_name -> google.protobuf.Value - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 5, // 1: authservice.config.v1.oidc.OIDCConfig.client_secret_ref:type_name -> authservice.config.v1.oidc.OIDCConfig.SecretReference + 0, // 2: authservice.config.v1.oidc.OIDCConfig.id_token:type_name -> authservice.config.v1.oidc.TokenConfig + 0, // 3: authservice.config.v1.oidc.OIDCConfig.access_token:type_name -> authservice.config.v1.oidc.TokenConfig + 2, // 4: authservice.config.v1.oidc.OIDCConfig.logout:type_name -> authservice.config.v1.oidc.LogoutConfig + 1, // 5: authservice.config.v1.oidc.OIDCConfig.redis_session_store_config:type_name -> authservice.config.v1.oidc.RedisConfig + 6, // 6: authservice.config.v1.oidc.OIDCConfig.skip_verify_peer_cert:type_name -> google.protobuf.Value + 6, // 7: authservice.config.v1.oidc.OIDCConfig.JwksFetcherConfig.skip_verify_peer_cert:type_name -> google.protobuf.Value + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_v1_oidc_config_proto_init() } @@ -879,10 +990,24 @@ func file_v1_oidc_config_proto_init() { return nil } } + file_v1_oidc_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OIDCConfig_SecretReference); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_v1_oidc_config_proto_msgTypes[3].OneofWrappers = []interface{}{ (*OIDCConfig_Jwks)(nil), (*OIDCConfig_JwksFetcher)(nil), + (*OIDCConfig_ClientSecret)(nil), + (*OIDCConfig_ClientSecretRef)(nil), (*OIDCConfig_TrustedCertificateAuthority)(nil), (*OIDCConfig_TrustedCertificateAuthorityFile)(nil), } @@ -892,7 +1017,7 @@ func file_v1_oidc_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_oidc_config_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/config/gen/go/v1/oidc/config.pb.validate.go b/config/gen/go/v1/oidc/config.pb.validate.go index 8957268..11b363c 100644 --- a/config/gen/go/v1/oidc/config.pb.validate.go +++ b/config/gen/go/v1/oidc/config.pb.validate.go @@ -428,17 +428,6 @@ func (m *OIDCConfig) validate(all bool) error { errors = append(errors, err) } - if utf8.RuneCountInString(m.GetClientSecret()) < 1 { - err := OIDCConfigValidationError{ - field: "ClientSecret", - reason: "value length must be at least 1 runes", - } - if !all { - return err - } - errors = append(errors, err) - } - // no validation rules for CookieNamePrefix if m.GetIdToken() == nil { @@ -660,6 +649,76 @@ func (m *OIDCConfig) validate(all bool) error { default: _ = v // ensures v is used } + oneofClientSecretConfigPresent := false + switch v := m.ClientSecretConfig.(type) { + case *OIDCConfig_ClientSecret: + if v == nil { + err := OIDCConfigValidationError{ + field: "ClientSecretConfig", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + oneofClientSecretConfigPresent = true + // no validation rules for ClientSecret + case *OIDCConfig_ClientSecretRef: + if v == nil { + err := OIDCConfigValidationError{ + field: "ClientSecretConfig", + reason: "oneof value cannot be a typed-nil", + } + if !all { + return err + } + errors = append(errors, err) + } + oneofClientSecretConfigPresent = true + + if all { + switch v := interface{}(m.GetClientSecretRef()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, OIDCConfigValidationError{ + field: "ClientSecretRef", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, OIDCConfigValidationError{ + field: "ClientSecretRef", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetClientSecretRef()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return OIDCConfigValidationError{ + field: "ClientSecretRef", + reason: "embedded message failed validation", + cause: err, + } + } + } + + default: + _ = v // ensures v is used + } + if !oneofClientSecretConfigPresent { + err := OIDCConfigValidationError{ + field: "ClientSecretConfig", + reason: "value is required", + } + if !all { + return err + } + errors = append(errors, err) + } switch v := m.TrustedCaConfig.(type) { case *OIDCConfig_TrustedCertificateAuthority: if v == nil { @@ -901,3 +960,118 @@ var _ interface { Cause() error ErrorName() string } = OIDCConfig_JwksFetcherConfigValidationError{} + +// Validate checks the field values on OIDCConfig_SecretReference with the +// rules defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *OIDCConfig_SecretReference) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on OIDCConfig_SecretReference with the +// rules defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// OIDCConfig_SecretReferenceMultiError, or nil if none found. +func (m *OIDCConfig_SecretReference) ValidateAll() error { + return m.validate(true) +} + +func (m *OIDCConfig_SecretReference) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Namespace + + if utf8.RuneCountInString(m.GetName()) < 1 { + err := OIDCConfig_SecretReferenceValidationError{ + field: "Name", + reason: "value length must be at least 1 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + if len(errors) > 0 { + return OIDCConfig_SecretReferenceMultiError(errors) + } + + return nil +} + +// OIDCConfig_SecretReferenceMultiError is an error wrapping multiple +// validation errors returned by OIDCConfig_SecretReference.ValidateAll() if +// the designated constraints aren't met. +type OIDCConfig_SecretReferenceMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m OIDCConfig_SecretReferenceMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m OIDCConfig_SecretReferenceMultiError) AllErrors() []error { return m } + +// OIDCConfig_SecretReferenceValidationError is the validation error returned +// by OIDCConfig_SecretReference.Validate if the designated constraints aren't met. +type OIDCConfig_SecretReferenceValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e OIDCConfig_SecretReferenceValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e OIDCConfig_SecretReferenceValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e OIDCConfig_SecretReferenceValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e OIDCConfig_SecretReferenceValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e OIDCConfig_SecretReferenceValidationError) ErrorName() string { + return "OIDCConfig_SecretReferenceValidationError" +} + +// Error satisfies the builtin error interface +func (e OIDCConfig_SecretReferenceValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sOIDCConfig_SecretReference.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = OIDCConfig_SecretReferenceValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = OIDCConfig_SecretReferenceValidationError{} diff --git a/config/v1/oidc/config.proto b/config/v1/oidc/config.proto index 1dba272..da00d7f 100644 --- a/config/v1/oidc/config.proto +++ b/config/v1/oidc/config.proto @@ -132,10 +132,30 @@ message OIDCConfig { // Required. string client_id = 5 [(validate.rules).string.min_len = 1]; - // The OIDC client secret assigned to the filter to be used in the - // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). - // Required. - string client_secret = 6 [(validate.rules).string.min_len = 1]; + // This message defines a reference to a Kubernetes Secret resource. + message SecretReference { + // The namespace of the referenced Secret, if not set, default to "default" namespace. + string namespace = 1; + + // The name of the referenced Secret. + string name = 2 [(validate.rules).string.min_len = 1]; + } + + oneof client_secret_config { + option(validate.required) = true; + // The OIDC client secret assigned to the filter to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // This field keeps the client secret in plain text. Recommend to use `client_secret_ref` instead + // when running in a Kubernetes cluster. + string client_secret = 6; + + // The Kubernetes secret that contains the OIDC client secret assigned to the filter to be used in the + // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). + // + // This is an Opaque secret. The client secret should be stored in the key "client-secret". + // This filed is only valid when running in a Kubernetes cluster. + SecretReference client_secret_ref = 21; + } // Additional scopes passed to the OIDC Provider in the // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). @@ -229,4 +249,3 @@ message OIDCConfig { // Optional. google.protobuf.Value skip_verify_peer_cert = 18; // keep this field out from the trusted_ca_config one of for backward compatibility. } - diff --git a/go.mod b/go.mod index bcd7917..aa725f2 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 k8s.io/client-go v0.29.2 + sigs.k8s.io/controller-runtime v0.17.2 ) require ( @@ -32,7 +33,9 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.8.0 // indirect + github.com/go-logr/logr v1.4.1 // 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.3 // indirect @@ -74,5 +77,5 @@ require ( k8s.io/utils v0.0.0-20230726121419-3b25d923346b // 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.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index c632294..57b62f3 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,13 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= +github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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.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= @@ -106,10 +111,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= +github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -141,6 +146,10 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +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.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 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= @@ -148,6 +157,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -234,6 +245,8 @@ 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.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= +k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= +k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= @@ -244,9 +257,11 @@ k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/A k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= +sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= 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= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/authz/oidc_test.go b/internal/authz/oidc_test.go index 6311a72..42db58b 100644 --- a/internal/authz/oidc_test.go +++ b/internal/authz/oidc_test.go @@ -140,8 +140,10 @@ var ( AuthorizationUri: "http://idp-test-server/auth", CallbackUri: "https://localhost:443/callback", ClientId: "test-client-id", - ClientSecret: "test-client-secret", - Scopes: []string{"openid", "email"}, + ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ + ClientSecret: "test-client-secret", + }, + Scopes: []string{"openid", "email"}, Logout: &oidcv1.LogoutConfig{ Path: "/logout", RedirectUri: "http://idp-test-server/logout?with-params", @@ -160,8 +162,10 @@ var ( ConfigurationUri: "http://idp-test-server/.well-known/openid-configuration", CallbackUri: "https://localhost:443/callback", ClientId: "test-client-id", - ClientSecret: "test-client-secret", - Scopes: []string{"openid", "email"}, + ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ + ClientSecret: "test-client-secret", + }, + Scopes: []string{"openid", "email"}, } wellKnownURIs = ` diff --git a/internal/config.go b/internal/config.go index f41d8f4..70e0051 100644 --- a/internal/config.go +++ b/internal/config.go @@ -15,6 +15,7 @@ package internal import ( + "context" "errors" "fmt" "net/url" @@ -25,6 +26,10 @@ import ( "github.com/tetratelabs/run" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" @@ -44,6 +49,9 @@ var ( ErrHealthPortInUse = errors.New("health port is already in use by listen port") ErrMustNotBeRootPath = errors.New("must not be root path") ErrMustBeDifferentPath = errors.New("must be different path") + + // kubeClient is the in-cluster Kubernetes client used to retrieve the client secret from a Kubernetes secret. + kubeClient client.Client ) // LocalConfigFile is a run.Config that loads the configuration file. @@ -117,6 +125,12 @@ func (l *LocalConfigFile) Validate() error { return err } + // Initialize the client secret from the Kubernetes secret if it is a referenced + // Kubernetes secret. + if err = initClientSecret(&l.Config); err != nil { + return err + } + // Now that all defaults are set and configurations are merged, validate all final settings return l.Config.ValidateAll() } @@ -277,3 +291,82 @@ func hasRootPath(uri string) bool { func isRootPath(path string) bool { return path == "/" || path == "" } + +func initClientSecret(cfg *configv1.Config) error { + var ( + err error + errs []error + ) + + for _, fc := range cfg.Chains { + for _, f := range fc.Filters { + if f.GetOidc() != nil && f.GetOidc().GetClientSecretRef() != nil { + if kubeClient == nil { + kubeClient, err = getKubeClient() + if err != nil { + return fmt.Errorf("error creating kube client: %w", err) + } + } + + clientSecret, err := getClientSecretFromK8s(f.GetOidc(), kubeClient) + if err != nil { + errs = append(errs, fmt.Errorf("error getting client secret: %w", err)) + } + + f.GetOidc().ClientSecretConfig = &oidcv1.OIDCConfig_ClientSecret{ + ClientSecret: clientSecret, + } + } + } + } + + return errors.Join(errs...) +} + +const ( + defaultNamespace = "default" + clientSecretKey = "client-secret" +) + +// getClientSecretFromK8s retrieves the client secret from the referenced Kubernetes secret. +func getClientSecretFromK8s(c *oidcv1.OIDCConfig, cl client.Client) (string, error) { + if c.GetClientSecretRef() == nil { + return "", fmt.Errorf("client secret ref not found") + } + + namespace := c.GetClientSecretRef().Namespace + if namespace == "" { + namespace = defaultNamespace + } + secretName := types.NamespacedName{ + Namespace: namespace, + Name: c.GetClientSecretRef().Name, + } + + secret := &v1.Secret{} + err := cl.Get(context.Background(), secretName, secret) + if err != nil { + return "", fmt.Errorf("error getting secret: %w", err) + } + + clientSecretBytes, ok := secret.Data[clientSecretKey] + if !ok || len(clientSecretBytes) == 0 { + return "", fmt.Errorf("client secret not found in secret %s", secretName.String()) + } + + return string(clientSecretBytes), nil +} + +func getKubeClient() (client.Client, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, fmt.Errorf("error getting kube config: %w", err) + } + + cl, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, fmt.Errorf("errot creating kube client: %w", err) + } + + return cl, nil +} diff --git a/internal/config_test.go b/internal/config_test.go index a4e496b..b334346 100644 --- a/internal/config_test.go +++ b/internal/config_test.go @@ -24,6 +24,9 @@ import ( "github.com/tetratelabs/telemetry" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" @@ -215,7 +218,7 @@ func TestLoadOIDC(t *testing.T) { }, }, ClientId: "fake-client-id", - ClientSecret: "fake-client-secret", + ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ClientSecret: "fake-client-secret"}, CookieNamePrefix: "", IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"}, ProxyUri: "http://fake", @@ -263,3 +266,96 @@ func TestConfigToJSONString(t *testing.T) { }) } } + +func TestLoadOIDCClientSecret(t *testing.T) { + validSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-secret", + }, + Data: map[string][]byte{ + clientSecretKey: []byte("fake-client-secret"), + }, + } + invalidSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "invalid-secret", + }, + Data: map[string][]byte{ + clientSecretKey + "-invalid": []byte("fake-client-secret"), + }, + } + + // inject kube client for the test + kubeClient = fake.NewClientBuilder().WithObjects(validSecret, invalidSecret).Build() + + tests := []struct { + name string + configFile string + want *configv1.Config + error string + }{ + { + name: "valid-secret", + configFile: "oidc-with-valid-secret-ref", + want: &configv1.Config{ + ListenAddress: "0.0.0.0", + ListenPort: 8080, + LogLevel: "debug", + Threads: 1, + Chains: []*configv1.FilterChain{ + { + Name: "oidc", + Filters: []*configv1.Filter{ + { + Type: &configv1.Filter_Oidc{ + Oidc: &oidcv1.OIDCConfig{ + AuthorizationUri: "http://fake", + TokenUri: "http://fake", + CallbackUri: "http://fake/callback", + JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"}, + ClientId: "fake-client-id", + ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ClientSecret: "fake-client-secret"}, + CookieNamePrefix: "", + IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"}, + ProxyUri: "http://fake", + RedisSessionStoreConfig: &oidcv1.RedisConfig{ServerUri: "redis://localhost:6379/0"}, + Scopes: []string{scopeOIDC}, + Logout: &oidcv1.LogoutConfig{Path: "/logout", RedirectUri: "http://fake"}, + }, + }, + }, + }, + }, + }, + }, + error: "", + }, + { + name: "valid-secret", + configFile: "oidc-with-invalid-secret-ref", + error: "client secret not found in secret default/invalid-secret", + }, + { + name: "valid-secret", + configFile: "oidc-with-non-existing-secret-ref", + error: "secrets \"non-existing-secret\" not found", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var cfg LocalConfigFile + g := run.Group{Logger: telemetry.NoopLogger()} + g.Register(&cfg) + err := g.Run("", "--config-path", fmt.Sprintf("testdata/%s.json", tc.configFile)) + if tc.error != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.error) + } else { + require.NoError(t, err) + require.True(t, proto.Equal(tc.want, &cfg.Config)) + } + }) + } +} diff --git a/internal/testdata/oidc-with-invalid-secret-ref.json b/internal/testdata/oidc-with-invalid-secret-ref.json new file mode 100644 index 0000000..0326524 --- /dev/null +++ b/internal/testdata/oidc-with-invalid-secret-ref.json @@ -0,0 +1,38 @@ + +{ + "listen_address": "0.0.0.0", + "listen_port": 8080, + "log_level": "debug", + "chains": [ + { + "name": "oidc", + "filters": [ + { + "oidc": { + "authorization_uri": "http://fake", + "token_uri": "http://fake", + "callback_uri": "http://fake/callback", + "proxy_uri": "http://fake", + "jwks": "fake-jwks", + "client_id": "fake-client-id", + "client_secret_ref": { + "namespace": "default", + "name": "invalid-secret" + }, + "id_token": { + "preamble": "Bearer", + "header": "authorization" + }, + "redis_session_store_config": { + "server_uri": "redis://localhost:6379/0" + }, + "logout": { + "path": "/logout", + "redirect_uri": "http://fake" + } + } + } + ] + } + ] +} diff --git a/internal/testdata/oidc-with-non-existing-secret-ref.json b/internal/testdata/oidc-with-non-existing-secret-ref.json new file mode 100644 index 0000000..01024a6 --- /dev/null +++ b/internal/testdata/oidc-with-non-existing-secret-ref.json @@ -0,0 +1,38 @@ + +{ + "listen_address": "0.0.0.0", + "listen_port": 8080, + "log_level": "debug", + "chains": [ + { + "name": "oidc", + "filters": [ + { + "oidc": { + "authorization_uri": "http://fake", + "token_uri": "http://fake", + "callback_uri": "http://fake/callback", + "proxy_uri": "http://fake", + "jwks": "fake-jwks", + "client_id": "fake-client-id", + "client_secret_ref": { + "namespace": "default", + "name": "non-existing-secret" + }, + "id_token": { + "preamble": "Bearer", + "header": "authorization" + }, + "redis_session_store_config": { + "server_uri": "redis://localhost:6379/0" + }, + "logout": { + "path": "/logout", + "redirect_uri": "http://fake" + } + } + } + ] + } + ] +} diff --git a/internal/testdata/oidc-with-valid-secret-ref.json b/internal/testdata/oidc-with-valid-secret-ref.json new file mode 100644 index 0000000..9ae1d13 --- /dev/null +++ b/internal/testdata/oidc-with-valid-secret-ref.json @@ -0,0 +1,38 @@ + +{ + "listen_address": "0.0.0.0", + "listen_port": 8080, + "log_level": "debug", + "chains": [ + { + "name": "oidc", + "filters": [ + { + "oidc": { + "authorization_uri": "http://fake", + "token_uri": "http://fake", + "callback_uri": "http://fake/callback", + "proxy_uri": "http://fake", + "jwks": "fake-jwks", + "client_id": "fake-client-id", + "client_secret_ref": { + "namespace": "default", + "name": "test-secret" + }, + "id_token": { + "preamble": "Bearer", + "header": "authorization" + }, + "redis_session_store_config": { + "server_uri": "redis://localhost:6379/0" + }, + "logout": { + "path": "/logout", + "redirect_uri": "http://fake" + } + } + } + ] + } + ] +} From fcfb4e6a4d57696bf9e9df31651dba978f53aeb4 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Fri, 23 Feb 2024 20:33:35 +0100 Subject: [PATCH 2/3] Refactor the secret loading into a prerunner and added e2e tests --- .gitignore | 2 +- cmd/main.go | 25 ++++ config/gen/go/v1/config.pb.go | 10 +- config/gen/go/v1/mock/config.pb.go | 5 +- config/gen/go/v1/oidc/config.pb.go | 5 +- e2e/istio/cluster/manifests/authservice.yaml | 38 ++++- internal/config.go | 93 ------------- internal/config_test.go | 96 ------------- internal/secrets.go | 112 +++++++++++++++ internal/secrets_test.go | 130 ++++++++++++++++++ .../oidc-with-invalid-secret-ref.json | 1 - .../testdata/oidc-with-valid-secret-ref.json | 5 + 12 files changed, 322 insertions(+), 200 deletions(-) create mode 100644 internal/secrets.go create mode 100644 internal/secrets_test.go diff --git a/.gitignore b/.gitignore index 7e9d0d4..e69e011 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ bin/ .makerc .vimrc logs/ -cluster/kubeconfig +**/cluster/kubeconfig certs/ diff --git a/cmd/main.go b/cmd/main.go index 55452cf..75ea1f2 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,6 +22,8 @@ import ( "github.com/tetratelabs/run" "github.com/tetratelabs/run/pkg/signal" "github.com/tetratelabs/telemetry" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" "github.com/tetrateio/authservice-go/internal" "github.com/tetrateio/authservice-go/internal/oidc" @@ -29,6 +31,12 @@ import ( ) func main() { + k8sClient, err := getKubeClient() + if err != nil { + fmt.Printf("error creting k8s client: %v\n", err) + os.Exit(-1) + } + var ( configFile = &internal.LocalConfigFile{} logging = internal.NewLogSystem(log.New(), &configFile.Config) @@ -37,6 +45,7 @@ func main() { envoyAuthz = server.NewExtAuthZFilter(&configFile.Config, jwks, sessions) authzServer = server.New(&configFile.Config, envoyAuthz.Register) healthz = server.NewHealthServer(&configFile.Config) + secrets = internal.NewSecretLoader(&configFile.Config, k8sClient) ) configLog := run.NewPreRunner("config-log", func() error { @@ -52,6 +61,7 @@ func main() { g.Register( configFile, // load the configuration logging, // set up the logging system + secrets, // load the secrets and update the configuration configLog, // log the configuration jwks, // start the JWKS provider sessions, // start the session store @@ -65,3 +75,18 @@ func main() { os.Exit(-1) } } + +// getKubeClient returns a new Kubernetes client used to load secrets. +func getKubeClient() (client.Client, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, fmt.Errorf("error getting kube config: %w", err) + } + + cl, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, fmt.Errorf("errot creating kube client: %w", err) + } + + return cl, nil +} diff --git a/config/gen/go/v1/config.pb.go b/config/gen/go/v1/config.pb.go index ce89240..2417fee 100644 --- a/config/gen/go/v1/config.pb.go +++ b/config/gen/go/v1/config.pb.go @@ -21,13 +21,15 @@ package configv1 import ( + reflect "reflect" + sync "sync" + _ "github.com/envoyproxy/protoc-gen-validate/validate" - mock "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" - oidc "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" + + mock "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" + oidc "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" ) const ( diff --git a/config/gen/go/v1/mock/config.pb.go b/config/gen/go/v1/mock/config.pb.go index 5b934e3..c8b9a8b 100644 --- a/config/gen/go/v1/mock/config.pb.go +++ b/config/gen/go/v1/mock/config.pb.go @@ -21,10 +21,11 @@ package mock import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( diff --git a/config/gen/go/v1/oidc/config.pb.go b/config/gen/go/v1/oidc/config.pb.go index e564656..a5063aa 100644 --- a/config/gen/go/v1/oidc/config.pb.go +++ b/config/gen/go/v1/oidc/config.pb.go @@ -21,12 +21,13 @@ package oidc import ( + reflect "reflect" + sync "sync" + _ "github.com/envoyproxy/protoc-gen-validate/validate" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" - reflect "reflect" - sync "sync" ) const ( diff --git a/e2e/istio/cluster/manifests/authservice.yaml b/e2e/istio/cluster/manifests/authservice.yaml index a43237f..447f7fb 100644 --- a/e2e/istio/cluster/manifests/authservice.yaml +++ b/e2e/istio/cluster/manifests/authservice.yaml @@ -94,6 +94,39 @@ spec: configMap: name: authservice-config --- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: authservice-secrets + namespace: authservice +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "watch", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: authservice-secrets + namespace: authservice +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: authservice-secrets +subjects: + - kind: ServiceAccount + name: authservice + namespace: authservice +--- +apiVersion: v1 +kind: Secret +metadata: + name: client-secret + namespace: authservice +type: Opaque +stringData: + client-secret: "authservice-secret" +--- kind: ConfigMap apiVersion: v1 metadata: @@ -116,7 +149,10 @@ data: "configuration_uri": "http://keycloak.keycloak:8080/realms/master/.well-known/openid-configuration", "callback_uri": "https://http-echo.authservice.internal/callback", "client_id": "authservice", - "client_secret": "authservice-secret", + "client_secret_ref": { + "namespace": "authservice", + "name": "client-secret" + }, "cookie_name_prefix": "authservice", "id_token": { "preamble": "Bearer", diff --git a/internal/config.go b/internal/config.go index 70e0051..f41d8f4 100644 --- a/internal/config.go +++ b/internal/config.go @@ -15,7 +15,6 @@ package internal import ( - "context" "errors" "fmt" "net/url" @@ -26,10 +25,6 @@ import ( "github.com/tetratelabs/run" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" @@ -49,9 +44,6 @@ var ( ErrHealthPortInUse = errors.New("health port is already in use by listen port") ErrMustNotBeRootPath = errors.New("must not be root path") ErrMustBeDifferentPath = errors.New("must be different path") - - // kubeClient is the in-cluster Kubernetes client used to retrieve the client secret from a Kubernetes secret. - kubeClient client.Client ) // LocalConfigFile is a run.Config that loads the configuration file. @@ -125,12 +117,6 @@ func (l *LocalConfigFile) Validate() error { return err } - // Initialize the client secret from the Kubernetes secret if it is a referenced - // Kubernetes secret. - if err = initClientSecret(&l.Config); err != nil { - return err - } - // Now that all defaults are set and configurations are merged, validate all final settings return l.Config.ValidateAll() } @@ -291,82 +277,3 @@ func hasRootPath(uri string) bool { func isRootPath(path string) bool { return path == "/" || path == "" } - -func initClientSecret(cfg *configv1.Config) error { - var ( - err error - errs []error - ) - - for _, fc := range cfg.Chains { - for _, f := range fc.Filters { - if f.GetOidc() != nil && f.GetOidc().GetClientSecretRef() != nil { - if kubeClient == nil { - kubeClient, err = getKubeClient() - if err != nil { - return fmt.Errorf("error creating kube client: %w", err) - } - } - - clientSecret, err := getClientSecretFromK8s(f.GetOidc(), kubeClient) - if err != nil { - errs = append(errs, fmt.Errorf("error getting client secret: %w", err)) - } - - f.GetOidc().ClientSecretConfig = &oidcv1.OIDCConfig_ClientSecret{ - ClientSecret: clientSecret, - } - } - } - } - - return errors.Join(errs...) -} - -const ( - defaultNamespace = "default" - clientSecretKey = "client-secret" -) - -// getClientSecretFromK8s retrieves the client secret from the referenced Kubernetes secret. -func getClientSecretFromK8s(c *oidcv1.OIDCConfig, cl client.Client) (string, error) { - if c.GetClientSecretRef() == nil { - return "", fmt.Errorf("client secret ref not found") - } - - namespace := c.GetClientSecretRef().Namespace - if namespace == "" { - namespace = defaultNamespace - } - secretName := types.NamespacedName{ - Namespace: namespace, - Name: c.GetClientSecretRef().Name, - } - - secret := &v1.Secret{} - err := cl.Get(context.Background(), secretName, secret) - if err != nil { - return "", fmt.Errorf("error getting secret: %w", err) - } - - clientSecretBytes, ok := secret.Data[clientSecretKey] - if !ok || len(clientSecretBytes) == 0 { - return "", fmt.Errorf("client secret not found in secret %s", secretName.String()) - } - - return string(clientSecretBytes), nil -} - -func getKubeClient() (client.Client, error) { - cfg, err := config.GetConfig() - if err != nil { - return nil, fmt.Errorf("error getting kube config: %w", err) - } - - cl, err := client.New(cfg, client.Options{}) - if err != nil { - return nil, fmt.Errorf("errot creating kube client: %w", err) - } - - return cl, nil -} diff --git a/internal/config_test.go b/internal/config_test.go index b334346..181dde4 100644 --- a/internal/config_test.go +++ b/internal/config_test.go @@ -24,9 +24,6 @@ import ( "github.com/tetratelabs/telemetry" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client/fake" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" @@ -266,96 +263,3 @@ func TestConfigToJSONString(t *testing.T) { }) } } - -func TestLoadOIDCClientSecret(t *testing.T) { - validSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "test-secret", - }, - Data: map[string][]byte{ - clientSecretKey: []byte("fake-client-secret"), - }, - } - invalidSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "invalid-secret", - }, - Data: map[string][]byte{ - clientSecretKey + "-invalid": []byte("fake-client-secret"), - }, - } - - // inject kube client for the test - kubeClient = fake.NewClientBuilder().WithObjects(validSecret, invalidSecret).Build() - - tests := []struct { - name string - configFile string - want *configv1.Config - error string - }{ - { - name: "valid-secret", - configFile: "oidc-with-valid-secret-ref", - want: &configv1.Config{ - ListenAddress: "0.0.0.0", - ListenPort: 8080, - LogLevel: "debug", - Threads: 1, - Chains: []*configv1.FilterChain{ - { - Name: "oidc", - Filters: []*configv1.Filter{ - { - Type: &configv1.Filter_Oidc{ - Oidc: &oidcv1.OIDCConfig{ - AuthorizationUri: "http://fake", - TokenUri: "http://fake", - CallbackUri: "http://fake/callback", - JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"}, - ClientId: "fake-client-id", - ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ClientSecret: "fake-client-secret"}, - CookieNamePrefix: "", - IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"}, - ProxyUri: "http://fake", - RedisSessionStoreConfig: &oidcv1.RedisConfig{ServerUri: "redis://localhost:6379/0"}, - Scopes: []string{scopeOIDC}, - Logout: &oidcv1.LogoutConfig{Path: "/logout", RedirectUri: "http://fake"}, - }, - }, - }, - }, - }, - }, - }, - error: "", - }, - { - name: "valid-secret", - configFile: "oidc-with-invalid-secret-ref", - error: "client secret not found in secret default/invalid-secret", - }, - { - name: "valid-secret", - configFile: "oidc-with-non-existing-secret-ref", - error: "secrets \"non-existing-secret\" not found", - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var cfg LocalConfigFile - g := run.Group{Logger: telemetry.NoopLogger()} - g.Register(&cfg) - err := g.Run("", "--config-path", fmt.Sprintf("testdata/%s.json", tc.configFile)) - if tc.error != "" { - require.Error(t, err) - require.Contains(t, err.Error(), tc.error) - } else { - require.NoError(t, err) - require.True(t, proto.Equal(tc.want, &cfg.Config)) - } - }) - } -} diff --git a/internal/secrets.go b/internal/secrets.go new file mode 100644 index 0000000..47163d9 --- /dev/null +++ b/internal/secrets.go @@ -0,0 +1,112 @@ +// Copyright 2024 Tetrate +// +// 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 internal + +import ( + "context" + "errors" + "fmt" + + "github.com/tetratelabs/run" + "github.com/tetratelabs/telemetry" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" + oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" +) + +const ( + defaultNamespace = "default" + clientSecretKey = "client-secret" +) + +var ( + _ run.PreRunner = (*SecretLoader)(nil) + + ErrGetSecret = errors.New("err getting secret") + ErrNoSecretData = errors.New("client-secret not found in secret") +) + +// SecretLoader is a pre-runner that loads secrets from Kubernetes and updates +// the configuration with the loaded data. +type SecretLoader struct { + log telemetry.Logger + cfg *configv1.Config + k8sClient client.Client +} + +// NewSecretLoader creates a new that loads secrets from Kubernetes and updates +// // the configuration with the loaded data. +func NewSecretLoader(cfg *configv1.Config, k8sClient client.Client) *SecretLoader { + return &SecretLoader{ + log: Logger(Config), + cfg: cfg, + k8sClient: k8sClient, + } +} + +// Name implements run.PreRunner +func (s *SecretLoader) Name() string { return "Secret loader" } + +// PreRun processes all the OIDC configurations and loads all required secrets from Kubernetes. +func (s *SecretLoader) PreRun() error { + var errs []error + for _, c := range s.cfg.Chains { + for _, f := range c.Filters { + oidcCfg, ok := f.Type.(*configv1.Filter_Oidc) + if !ok || oidcCfg.Oidc.GetClientSecretRef().GetName() == "" { + continue + } + + errs = append(errs, s.loadClientSecretFromK8s(oidcCfg.Oidc)) + } + } + + return errors.Join(errs...) +} + +// loadClientSecretFromK8s retrieves the client secret from the referenced Kubernetes secret. +func (s *SecretLoader) loadClientSecretFromK8s(cfg *oidcv1.OIDCConfig) error { + namespace := cfg.GetClientSecretRef().Namespace + if namespace == "" { + namespace = defaultNamespace + } + secretName := types.NamespacedName{ + Namespace: namespace, + Name: cfg.GetClientSecretRef().GetName(), + } + + s.log.Info("loading client-secret from secret", + "secret", secretName.String(), "client-id", cfg.GetClientId()) + + secret := &corev1.Secret{} + if err := s.k8sClient.Get(context.Background(), secretName, secret); err != nil { + return fmt.Errorf("%w: %w", ErrGetSecret, err) + } + + clientSecretBytes, ok := secret.Data[clientSecretKey] + if !ok || len(clientSecretBytes) == 0 { + return fmt.Errorf("%w: %s", ErrNoSecretData, secretName.String()) + } + + // Update the configuration with the loaded client secret + cfg.ClientSecretConfig = &oidcv1.OIDCConfig_ClientSecret{ + ClientSecret: string(clientSecretBytes), + } + + return nil +} diff --git a/internal/secrets_test.go b/internal/secrets_test.go new file mode 100644 index 0000000..6a947ae --- /dev/null +++ b/internal/secrets_test.go @@ -0,0 +1,130 @@ +// Copyright 2024 Tetrate +// +// 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 internal + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tetratelabs/run" + "github.com/tetratelabs/telemetry" + "google.golang.org/protobuf/proto" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" + mockv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/mock" + oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" +) + +func TestLoadOIDCClientSecret(t *testing.T) { + validSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "test-secret", + }, + Data: map[string][]byte{ + clientSecretKey: []byte("fake-client-secret"), + }, + } + invalidSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "invalid-secret", + }, + Data: map[string][]byte{ + clientSecretKey + "-invalid": []byte("fake-client-secret"), + }, + } + + kubeClient := fake.NewClientBuilder().WithObjects(validSecret, invalidSecret).Build() + + tests := []struct { + name string + configFile string + want *configv1.Config + err error + }{ + { + name: "valid-secret", + configFile: "oidc-with-valid-secret-ref", + want: &configv1.Config{ + ListenAddress: "0.0.0.0", + ListenPort: 8080, + LogLevel: "debug", + Threads: 1, + Chains: []*configv1.FilterChain{ + { + Name: "oidc", + Filters: []*configv1.Filter{ + { + Type: &configv1.Filter_Mock{ + Mock: &mockv1.MockConfig{ + Allow: true, + }, + }, + }, + { + Type: &configv1.Filter_Oidc{ + Oidc: &oidcv1.OIDCConfig{ + AuthorizationUri: "http://fake", + TokenUri: "http://fake", + CallbackUri: "http://fake/callback", + JwksConfig: &oidcv1.OIDCConfig_Jwks{Jwks: "fake-jwks"}, + ClientId: "fake-client-id", + ClientSecretConfig: &oidcv1.OIDCConfig_ClientSecret{ClientSecret: "fake-client-secret"}, + CookieNamePrefix: "", + IdToken: &oidcv1.TokenConfig{Preamble: "Bearer", Header: "authorization"}, + ProxyUri: "http://fake", + RedisSessionStoreConfig: &oidcv1.RedisConfig{ServerUri: "redis://localhost:6379/0"}, + Scopes: []string{scopeOIDC}, + Logout: &oidcv1.LogoutConfig{Path: "/logout", RedirectUri: "http://fake"}, + }, + }, + }, + }, + }, + }, + }, + err: nil, + }, + { + name: "invalid-secret", + configFile: "oidc-with-invalid-secret-ref", + err: ErrNoSecretData, + }, + { + name: "not-found-secret", + configFile: "oidc-with-non-existing-secret-ref", + err: ErrGetSecret, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var cfg LocalConfigFile + g := run.Group{Logger: telemetry.NoopLogger()} + g.Register(&cfg, NewSecretLoader(&cfg.Config, kubeClient)) + err := g.Run("", "--config-path", fmt.Sprintf("testdata/%s.json", tt.configFile)) + + require.ErrorIs(t, err, tt.err) + if tt.err == nil { + require.True(t, proto.Equal(tt.want, &cfg.Config)) + } + }) + } +} diff --git a/internal/testdata/oidc-with-invalid-secret-ref.json b/internal/testdata/oidc-with-invalid-secret-ref.json index 0326524..6ab0fc2 100644 --- a/internal/testdata/oidc-with-invalid-secret-ref.json +++ b/internal/testdata/oidc-with-invalid-secret-ref.json @@ -16,7 +16,6 @@ "jwks": "fake-jwks", "client_id": "fake-client-id", "client_secret_ref": { - "namespace": "default", "name": "invalid-secret" }, "id_token": { diff --git a/internal/testdata/oidc-with-valid-secret-ref.json b/internal/testdata/oidc-with-valid-secret-ref.json index 9ae1d13..1d23400 100644 --- a/internal/testdata/oidc-with-valid-secret-ref.json +++ b/internal/testdata/oidc-with-valid-secret-ref.json @@ -7,6 +7,11 @@ { "name": "oidc", "filters": [ + { + "mock": { + "allow": true + } + }, { "oidc": { "authorization_uri": "http://fake", From 3e45ec50cfaf96ed6c9397dad84f72811ba550f3 Mon Sep 17 00:00:00 2001 From: Ignasi Barrera Date: Sat, 24 Feb 2024 07:01:18 +0100 Subject: [PATCH 3/3] Only load kubeconfig if in k8s --- cmd/main.go | 25 +------------------------ internal/secrets.go | 36 ++++++++++++++++++++++++++++++------ internal/secrets_test.go | 17 ++++++++++++++++- 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 75ea1f2..537ccb5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -22,8 +22,6 @@ import ( "github.com/tetratelabs/run" "github.com/tetratelabs/run/pkg/signal" "github.com/tetratelabs/telemetry" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/config" "github.com/tetrateio/authservice-go/internal" "github.com/tetrateio/authservice-go/internal/oidc" @@ -31,12 +29,6 @@ import ( ) func main() { - k8sClient, err := getKubeClient() - if err != nil { - fmt.Printf("error creting k8s client: %v\n", err) - os.Exit(-1) - } - var ( configFile = &internal.LocalConfigFile{} logging = internal.NewLogSystem(log.New(), &configFile.Config) @@ -45,7 +37,7 @@ func main() { envoyAuthz = server.NewExtAuthZFilter(&configFile.Config, jwks, sessions) authzServer = server.New(&configFile.Config, envoyAuthz.Register) healthz = server.NewHealthServer(&configFile.Config) - secrets = internal.NewSecretLoader(&configFile.Config, k8sClient) + secrets = internal.NewSecretLoader(&configFile.Config) ) configLog := run.NewPreRunner("config-log", func() error { @@ -75,18 +67,3 @@ func main() { os.Exit(-1) } } - -// getKubeClient returns a new Kubernetes client used to load secrets. -func getKubeClient() (client.Client, error) { - cfg, err := config.GetConfig() - if err != nil { - return nil, fmt.Errorf("error getting kube config: %w", err) - } - - cl, err := client.New(cfg, client.Options{}) - if err != nil { - return nil, fmt.Errorf("errot creating kube client: %w", err) - } - - return cl, nil -} diff --git a/internal/secrets.go b/internal/secrets.go index 47163d9..8003566 100644 --- a/internal/secrets.go +++ b/internal/secrets.go @@ -24,6 +24,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/config" configv1 "github.com/tetrateio/authservice-go/config/gen/go/v1" oidcv1 "github.com/tetrateio/authservice-go/config/gen/go/v1/oidc" @@ -37,8 +38,9 @@ const ( var ( _ run.PreRunner = (*SecretLoader)(nil) - ErrGetSecret = errors.New("err getting secret") - ErrNoSecretData = errors.New("client-secret not found in secret") + ErrLoadingKubeConfig = errors.New("error loading kube config") + ErrGetSecret = errors.New("err getting secret") + ErrNoSecretData = errors.New("client-secret not found in secret") ) // SecretLoader is a pre-runner that loads secrets from Kubernetes and updates @@ -51,11 +53,10 @@ type SecretLoader struct { // NewSecretLoader creates a new that loads secrets from Kubernetes and updates // // the configuration with the loaded data. -func NewSecretLoader(cfg *configv1.Config, k8sClient client.Client) *SecretLoader { +func NewSecretLoader(cfg *configv1.Config) *SecretLoader { return &SecretLoader{ - log: Logger(Config), - cfg: cfg, - k8sClient: k8sClient, + log: Logger(Config), + cfg: cfg, } } @@ -72,6 +73,14 @@ func (s *SecretLoader) PreRun() error { continue } + if s.k8sClient == nil { + var err error + s.k8sClient, err = getKubeClient() + if err != nil { + return fmt.Errorf("%w: loading client secret from k8s: %w", ErrLoadingKubeConfig, err) + } + } + errs = append(errs, s.loadClientSecretFromK8s(oidcCfg.Oidc)) } } @@ -110,3 +119,18 @@ func (s *SecretLoader) loadClientSecretFromK8s(cfg *oidcv1.OIDCConfig) error { return nil } + +// getKubeClient returns a new Kubernetes client used to load secrets. +func getKubeClient() (client.Client, error) { + cfg, err := config.GetConfig() + if err != nil { + return nil, fmt.Errorf("error getting kube config: %w", err) + } + + cl, err := client.New(cfg, client.Options{}) + if err != nil { + return nil, fmt.Errorf("errot creating kube client: %w", err) + } + + return cl, nil +} diff --git a/internal/secrets_test.go b/internal/secrets_test.go index 6a947ae..573b27c 100644 --- a/internal/secrets_test.go +++ b/internal/secrets_test.go @@ -117,8 +117,10 @@ func TestLoadOIDCClientSecret(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var cfg LocalConfigFile + sl := NewSecretLoader(&cfg.Config) + sl.k8sClient = kubeClient g := run.Group{Logger: telemetry.NoopLogger()} - g.Register(&cfg, NewSecretLoader(&cfg.Config, kubeClient)) + g.Register(&cfg, sl) err := g.Run("", "--config-path", fmt.Sprintf("testdata/%s.json", tt.configFile)) require.ErrorIs(t, err, tt.err) @@ -128,3 +130,16 @@ func TestLoadOIDCClientSecret(t *testing.T) { }) } } + +func TestErrLoadingKubeConfig(t *testing.T) { + t.Setenv("KUBECONFIG", "non-existing-file") + + var cfg LocalConfigFile + sl := NewSecretLoader(&cfg.Config) + + g := run.Group{Logger: telemetry.NoopLogger()} + g.Register(&cfg, sl) + err := g.Run("", "--config-path", "testdata/oidc-with-valid-secret-ref.json") + + require.ErrorContains(t, err, ErrLoadingKubeConfig.Error()) +}