From 19b23a9679655be7890e92ebb65b7598dd057287 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 6 Oct 2023 14:16:19 +0200 Subject: [PATCH 01/25] Add ForwardingSignatureValidator ForwardingSignatureValidator holds a pointer to some other SignatureValidator and will forward ValidateSignature requests to this validator. The validator it references can be changed by calling Replace(). --- pkg/jwt/BUILD.bazel | 1 + pkg/jwt/forwarding_signature_validator.go | 26 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 pkg/jwt/forwarding_signature_validator.go diff --git a/pkg/jwt/BUILD.bazel b/pkg/jwt/BUILD.bazel index 9ca0ddaa..71a16ce6 100644 --- a/pkg/jwt/BUILD.bazel +++ b/pkg/jwt/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "ecdsa_sha_signature_generator.go", "ecdsa_sha_signature_validator.go", "ed25519_signature_validator.go", + "forwarding_signature_validator.go", "generate_authorization_header.go", "hmac_sha_signature_validator.go", "rsa_sha_signature_validator.go", diff --git a/pkg/jwt/forwarding_signature_validator.go b/pkg/jwt/forwarding_signature_validator.go new file mode 100644 index 00000000..f6d59082 --- /dev/null +++ b/pkg/jwt/forwarding_signature_validator.go @@ -0,0 +1,26 @@ +package jwt + +import ( + "sync/atomic" +) + +type forwardingSignatureValidator struct { + validator atomic.Pointer[SignatureValidator] +} + +// NewForwardingSignatureValidator creates a SignatureValidator that simply forwards +// requests to another SignatureValidator. +func NewForwardingSignatureValidator(validator SignatureValidator) SignatureValidator { + sv := &forwardingSignatureValidator{} + sv.validator.Store(&validator) + + return sv +} + +func (sv *forwardingSignatureValidator) Replace(validator SignatureValidator) { + sv.validator.Store(&validator) +} + +func (sv *forwardingSignatureValidator) ValidateSignature(algorithm string, keyID *string, headerAndPayload string, signature []byte) bool { + return (*sv.validator.Load()).ValidateSignature(algorithm, keyID, headerAndPayload, signature) +} From 6c61cd443ecd17cde0a86967f101ada0d5675cd8 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 6 Oct 2023 15:23:10 +0200 Subject: [PATCH 02/25] Make JWKS a oneoff with either inline or file content This changes the jwt.proto definition to take either an inline JWKS struct, or a message containing a file path and a refresh interval. The intention is that when a file path and refresh interval is provided, we create a ForwardingSignatureValidator, and periodically update its internal SignatureValidator with content from the referenced file. When passing inline content, behavior should remain unchanged. --- pkg/proto/configuration/jwt/BUILD.bazel | 1 + pkg/proto/configuration/jwt/jwt.pb.go | 226 +++++++++++++++++++----- pkg/proto/configuration/jwt/jwt.proto | 37 ++-- 3 files changed, 204 insertions(+), 60 deletions(-) diff --git a/pkg/proto/configuration/jwt/BUILD.bazel b/pkg/proto/configuration/jwt/BUILD.bazel index 08c0a5a8..691b652f 100644 --- a/pkg/proto/configuration/jwt/BUILD.bazel +++ b/pkg/proto/configuration/jwt/BUILD.bazel @@ -8,6 +8,7 @@ proto_library( visibility = ["//visibility:public"], deps = [ "//pkg/proto/configuration/eviction:eviction_proto", + "@com_google_protobuf//:duration_proto", "@com_google_protobuf//:struct_proto", ], ) diff --git a/pkg/proto/configuration/jwt/jwt.pb.go b/pkg/proto/configuration/jwt/jwt.pb.go index f770ccb0..1f671712 100644 --- a/pkg/proto/configuration/jwt/jwt.pb.go +++ b/pkg/proto/configuration/jwt/jwt.pb.go @@ -10,6 +10,7 @@ import ( eviction "github.com/buildbarn/bb-storage/pkg/proto/configuration/eviction" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" structpb "google.golang.org/protobuf/types/known/structpb" reflect "reflect" sync "sync" @@ -27,11 +28,15 @@ type AuthorizationHeaderParserConfiguration struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - JwksInline *structpb.Struct `protobuf:"bytes,7,opt,name=jwks_inline,json=jwksInline,proto3" json:"jwks_inline,omitempty"` - MaximumCacheSize int32 `protobuf:"varint,3,opt,name=maximum_cache_size,json=maximumCacheSize,proto3" json:"maximum_cache_size,omitempty"` - CacheReplacementPolicy eviction.CacheReplacementPolicy `protobuf:"varint,4,opt,name=cache_replacement_policy,json=cacheReplacementPolicy,proto3,enum=buildbarn.configuration.eviction.CacheReplacementPolicy" json:"cache_replacement_policy,omitempty"` - ClaimsValidationJmespathExpression string `protobuf:"bytes,5,opt,name=claims_validation_jmespath_expression,json=claimsValidationJmespathExpression,proto3" json:"claims_validation_jmespath_expression,omitempty"` - MetadataExtractionJmespathExpression string `protobuf:"bytes,6,opt,name=metadata_extraction_jmespath_expression,json=metadataExtractionJmespathExpression,proto3" json:"metadata_extraction_jmespath_expression,omitempty"` + // Types that are assignable to Jwks: + // + // *AuthorizationHeaderParserConfiguration_JwksInline + // *AuthorizationHeaderParserConfiguration_JwksFile_ + Jwks isAuthorizationHeaderParserConfiguration_Jwks `protobuf_oneof:"jwks"` + MaximumCacheSize int32 `protobuf:"varint,3,opt,name=maximum_cache_size,json=maximumCacheSize,proto3" json:"maximum_cache_size,omitempty"` + CacheReplacementPolicy eviction.CacheReplacementPolicy `protobuf:"varint,4,opt,name=cache_replacement_policy,json=cacheReplacementPolicy,proto3,enum=buildbarn.configuration.eviction.CacheReplacementPolicy" json:"cache_replacement_policy,omitempty"` + ClaimsValidationJmespathExpression string `protobuf:"bytes,5,opt,name=claims_validation_jmespath_expression,json=claimsValidationJmespathExpression,proto3" json:"claims_validation_jmespath_expression,omitempty"` + MetadataExtractionJmespathExpression string `protobuf:"bytes,6,opt,name=metadata_extraction_jmespath_expression,json=metadataExtractionJmespathExpression,proto3" json:"metadata_extraction_jmespath_expression,omitempty"` } func (x *AuthorizationHeaderParserConfiguration) Reset() { @@ -66,13 +71,27 @@ func (*AuthorizationHeaderParserConfiguration) Descriptor() ([]byte, []int) { return file_pkg_proto_configuration_jwt_jwt_proto_rawDescGZIP(), []int{0} } +func (m *AuthorizationHeaderParserConfiguration) GetJwks() isAuthorizationHeaderParserConfiguration_Jwks { + if m != nil { + return m.Jwks + } + return nil +} + func (x *AuthorizationHeaderParserConfiguration) GetJwksInline() *structpb.Struct { - if x != nil { + if x, ok := x.GetJwks().(*AuthorizationHeaderParserConfiguration_JwksInline); ok { return x.JwksInline } return nil } +func (x *AuthorizationHeaderParserConfiguration) GetJwksFile() *AuthorizationHeaderParserConfiguration_JwksFile { + if x, ok := x.GetJwks().(*AuthorizationHeaderParserConfiguration_JwksFile_); ok { + return x.JwksFile + } + return nil +} + func (x *AuthorizationHeaderParserConfiguration) GetMaximumCacheSize() int32 { if x != nil { return x.MaximumCacheSize @@ -101,6 +120,79 @@ func (x *AuthorizationHeaderParserConfiguration) GetMetadataExtractionJmespathEx return "" } +type isAuthorizationHeaderParserConfiguration_Jwks interface { + isAuthorizationHeaderParserConfiguration_Jwks() +} + +type AuthorizationHeaderParserConfiguration_JwksInline struct { + JwksInline *structpb.Struct `protobuf:"bytes,7,opt,name=jwks_inline,json=jwksInline,proto3,oneof"` +} + +type AuthorizationHeaderParserConfiguration_JwksFile_ struct { + JwksFile *AuthorizationHeaderParserConfiguration_JwksFile `protobuf:"bytes,8,opt,name=jwks_file,json=jwksFile,proto3,oneof"` +} + +func (*AuthorizationHeaderParserConfiguration_JwksInline) isAuthorizationHeaderParserConfiguration_Jwks() { +} + +func (*AuthorizationHeaderParserConfiguration_JwksFile_) isAuthorizationHeaderParserConfiguration_Jwks() { +} + +type AuthorizationHeaderParserConfiguration_JwksFile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` + RefreshInterval *durationpb.Duration `protobuf:"bytes,2,opt,name=refresh_interval,json=refreshInterval,proto3" json:"refresh_interval,omitempty"` +} + +func (x *AuthorizationHeaderParserConfiguration_JwksFile) Reset() { + *x = AuthorizationHeaderParserConfiguration_JwksFile{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AuthorizationHeaderParserConfiguration_JwksFile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AuthorizationHeaderParserConfiguration_JwksFile) ProtoMessage() {} + +func (x *AuthorizationHeaderParserConfiguration_JwksFile) ProtoReflect() protoreflect.Message { + mi := &file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1] + 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 AuthorizationHeaderParserConfiguration_JwksFile.ProtoReflect.Descriptor instead. +func (*AuthorizationHeaderParserConfiguration_JwksFile) Descriptor() ([]byte, []int) { + return file_pkg_proto_configuration_jwt_jwt_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *AuthorizationHeaderParserConfiguration_JwksFile) GetFilePath() string { + if x != nil { + return x.FilePath + } + return "" +} + +func (x *AuthorizationHeaderParserConfiguration_JwksFile) GetRefreshInterval() *durationpb.Duration { + if x != nil { + return x.RefreshInterval + } + return nil +} + var File_pkg_proto_configuration_jwt_jwt_proto protoreflect.FileDescriptor var file_pkg_proto_configuration_jwt_jwt_proto_rawDesc = []byte{ @@ -108,44 +200,60 @@ var file_pkg_proto_configuration_jwt_jwt_proto_rawDesc = []byte{ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x77, 0x74, 0x2f, 0x6a, 0x77, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x6a, 0x77, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x2e, 0x6a, 0x77, 0x74, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xba, 0x03, 0x0a, 0x26, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x05, 0x0a, 0x26, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x73, 0x65, - 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0a, 0x6a, 0x77, - 0x6b, 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x69, - 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x61, 0x63, - 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x72, 0x0a, 0x18, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, - 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x16, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x25, 0x63, 0x6c, - 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x63, 0x6c, 0x61, 0x69, 0x6d, - 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x55, 0x0a, - 0x27, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, - 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x24, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, - 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2f, 0x62, 0x62, 0x2d, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x77, 0x74, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, + 0x6a, 0x77, 0x6b, 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x6b, 0x0a, 0x09, 0x6a, 0x77, + 0x6b, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x4c, 0x2e, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6a, 0x77, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, + 0x61, 0x72, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6a, + 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x69, 0x6d, + 0x75, 0x6d, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x61, 0x63, 0x68, + 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x72, 0x0a, 0x18, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x72, + 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, + 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x16, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x25, 0x63, 0x6c, 0x61, + 0x69, 0x6d, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6a, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x55, 0x0a, 0x27, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x24, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x1a, 0x6d, 0x0a, 0x08, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x10, + 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, + 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2f, 0x62, + 0x62, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x6a, 0x77, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -160,20 +268,24 @@ func file_pkg_proto_configuration_jwt_jwt_proto_rawDescGZIP() []byte { return file_pkg_proto_configuration_jwt_jwt_proto_rawDescData } -var file_pkg_proto_configuration_jwt_jwt_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_pkg_proto_configuration_jwt_jwt_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_pkg_proto_configuration_jwt_jwt_proto_goTypes = []interface{}{ - (*AuthorizationHeaderParserConfiguration)(nil), // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration - (*structpb.Struct)(nil), // 1: google.protobuf.Struct - (eviction.CacheReplacementPolicy)(0), // 2: buildbarn.configuration.eviction.CacheReplacementPolicy + (*AuthorizationHeaderParserConfiguration)(nil), // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration + (*AuthorizationHeaderParserConfiguration_JwksFile)(nil), // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile + (*structpb.Struct)(nil), // 2: google.protobuf.Struct + (eviction.CacheReplacementPolicy)(0), // 3: buildbarn.configuration.eviction.CacheReplacementPolicy + (*durationpb.Duration)(nil), // 4: google.protobuf.Duration } var file_pkg_proto_configuration_jwt_jwt_proto_depIdxs = []int32{ - 1, // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_inline:type_name -> google.protobuf.Struct - 2, // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.cache_replacement_policy:type_name -> buildbarn.configuration.eviction.CacheReplacementPolicy - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_inline:type_name -> google.protobuf.Struct + 1, // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_file:type_name -> buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile + 3, // 2: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.cache_replacement_policy:type_name -> buildbarn.configuration.eviction.CacheReplacementPolicy + 4, // 3: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile.refresh_interval:type_name -> google.protobuf.Duration + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_pkg_proto_configuration_jwt_jwt_proto_init() } @@ -194,6 +306,22 @@ func file_pkg_proto_configuration_jwt_jwt_proto_init() { return nil } } + file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AuthorizationHeaderParserConfiguration_JwksFile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*AuthorizationHeaderParserConfiguration_JwksInline)(nil), + (*AuthorizationHeaderParserConfiguration_JwksFile_)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -201,7 +329,7 @@ func file_pkg_proto_configuration_jwt_jwt_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pkg_proto_configuration_jwt_jwt_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/configuration/jwt/jwt.proto b/pkg/proto/configuration/jwt/jwt.proto index 5792267e..31b8c233 100644 --- a/pkg/proto/configuration/jwt/jwt.proto +++ b/pkg/proto/configuration/jwt/jwt.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package buildbarn.configuration.jwt; +import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "pkg/proto/configuration/eviction/eviction.proto"; @@ -14,17 +15,31 @@ message AuthorizationHeaderParserConfiguration { // Was 'public_key'. Replaced by 'jwks_inline'. reserved 2; - // A JSON Web Key Set (JWKS) as described in RFC 7517, chapter 5, - // containing one or more public keys against which tokens signatures - // are validated. - // - // Public keys encoded in DER or PEM format can be converted to a JSON - // Web Key Set using one of the following tools: - // - // - https://smallstep.com/docs/step-cli/reference/crypto/key/format/ - // - https://irrte.ch/jwt-js-decode/pem2jwk.html - // - https://russelldavies.github.io/jwk-creator/ - google.protobuf.Struct jwks_inline = 7; + message JwksFile { + // Path to JWKS file. + string file_path = 1; + + // Interval at which to refresh JWKS file. + google.protobuf.Duration refresh_interval = 2; + } + + oneof jwks { + // A JSON Web Key Set (JWKS) as described in RFC 7517, chapter 5, + // containing one or more public keys against which tokens signatures + // are validated. + // + // Public keys encoded in DER or PEM format can be converted to a JSON + // Web Key Set using one of the following tools: + // + // - https://smallstep.com/docs/step-cli/reference/crypto/key/format/ + // - https://irrte.ch/jwt-js-decode/pem2jwk.html + // - https://russelldavies.github.io/jwk-creator/ + google.protobuf.Struct jwks_inline = 7; + + // Similar to jwks_inline, but reads the JWKS from a file, with a + // refresh interval. + JwksFile jwks_file = 8; + } // Maximum number of validated tokens to cache in memory. This speeds // up successive requests made with the same token. From 67b1d0f4206eb36e8af62a916ab2551821ac7f6d Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 6 Oct 2023 15:30:12 +0200 Subject: [PATCH 03/25] First pass at loading JWKS file When loading the JWT configuration, check if the config provides inline JWKS content, or a reference to a file. If we get a reference to a file, we set up a goroutine to periodically fetch the file and update a ForwardingSignatureValidator. --- pkg/jwt/configuration.go | 70 +++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index e27ded4f..d884b747 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -5,7 +5,10 @@ import ( "crypto/ed25519" "crypto/rsa" "encoding/json" + "log" + "os" "reflect" + "time" "github.com/buildbarn/bb-storage/pkg/clock" "github.com/buildbarn/bb-storage/pkg/eviction" @@ -23,19 +26,29 @@ import ( // "Authorization" header parser based on options stored in a // configuration file. func NewAuthorizationHeaderParserFromConfiguration(config *configuration.AuthorizationHeaderParserConfiguration) (*AuthorizationHeaderParser, error) { - jwksJSON, err := protojson.Marshal(config.JwksInline) - if err != nil { - return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to marshal JSON Web Key Set") - } - var jwks jose.JSONWebKeySet - if err := json.Unmarshal(jwksJSON, &jwks); err != nil { - return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to unmarshal JSON Web Key Set") - } - signatureValidator, err := NewSignatureValidatorFromJSONWebKeySet(&jwks) - if err != nil { - return nil, err + var signatureValidator SignatureValidator + + switch key := config.Jwks.(type) { + case *configuration.AuthorizationHeaderParserConfiguration_JwksInline: + jwksJSON, err := protojson.Marshal(key.JwksInline) + if err != nil { + return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to marshal JSON Web Key Set") + } + var jwks jose.JSONWebKeySet + if err := json.Unmarshal(jwksJSON, &jwks); err != nil { + return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to unmarshal JSON Web Key Set") + } + signatureValidator, err = NewSignatureValidatorFromJSONWebKeySet(&jwks) + if err != nil { + return nil, err + } + case *configuration.AuthorizationHeaderParserConfiguration_JwksFile_: + signatureValidator = NewSignatureValidatorFromJSONWebKeySetFile(key.JwksFile.FilePath, key.JwksFile.RefreshInterval.AsDuration()) + default: + return nil, status.Error(codes.InvalidArgument, "No key type provided") } + evictionSet, err := eviction.NewSetFromConfiguration[string](config.CacheReplacementPolicy) if err != nil { return nil, util.StatusWrap(err, "Failed to create eviction set") @@ -102,3 +115,38 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature return NewDemultiplexingSignatureValidator(namedSignatureValidators, allSignatureValidators), nil } + +func NewSignatureValidatorFromJSONWebKeySetFile(path string, refreshInterval time.Duration) SignatureValidator { + // Hmm. I don't want us to first read the file once, to initialize this, and then read the file periodically to update it. + // However, I also don't want us to initialize this in a state that isn't ready for use. + validator := NewForwardingSignatureValidator(nil) + + // TODO: Run this as part of the program.Group, so that it gets + // cleaned up upon shutdown. + go func() { + t := time.NewTicker(refreshInterval) + for range t.C { + jwksJSON, err := os.ReadFile(path) + if err != nil { + log.Printf("Failed to reload JWKS file: %v", err) + continue + } + + var jwks jose.JSONWebKeySet + if err := json.Unmarshal(jwksJSON, &jwks); err != nil { + log.Printf("Failed to reload JWKS file: %v", err) + continue + } + + signatureValidator, err := NewSignatureValidatorFromJSONWebKeySet(&jwks) + if err != nil { + log.Printf("Failed to create SignatureValidator for JWKS file: %v", err) + continue + } + + (validator.(*forwardingSignatureValidator)).Replace(signatureValidator) + } + }() + + return validator +} From b35cd96d74d7148efdb57b902b02f86131e2031b Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sun, 8 Oct 2023 12:48:35 +0200 Subject: [PATCH 04/25] Remove RefreshInterval from JWKS config Rather than make this configurable, we'll refresh the JWKS file every 300 seconds --- pkg/jwt/configuration.go | 9 +- pkg/proto/configuration/jwt/jwt.pb.go | 185 +++++++------------------- pkg/proto/configuration/jwt/jwt.proto | 14 +- 3 files changed, 58 insertions(+), 150 deletions(-) mode change 100644 => 100755 pkg/proto/configuration/jwt/jwt.pb.go diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index d884b747..b9b50a3e 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -42,13 +42,12 @@ func NewAuthorizationHeaderParserFromConfiguration(config *configuration.Authori if err != nil { return nil, err } - case *configuration.AuthorizationHeaderParserConfiguration_JwksFile_: - signatureValidator = NewSignatureValidatorFromJSONWebKeySetFile(key.JwksFile.FilePath, key.JwksFile.RefreshInterval.AsDuration()) + case *configuration.AuthorizationHeaderParserConfiguration_JwksFile: + signatureValidator = NewSignatureValidatorFromJSONWebKeySetFile(key.JwksFile) default: return nil, status.Error(codes.InvalidArgument, "No key type provided") } - evictionSet, err := eviction.NewSetFromConfiguration[string](config.CacheReplacementPolicy) if err != nil { return nil, util.StatusWrap(err, "Failed to create eviction set") @@ -116,7 +115,7 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature return NewDemultiplexingSignatureValidator(namedSignatureValidators, allSignatureValidators), nil } -func NewSignatureValidatorFromJSONWebKeySetFile(path string, refreshInterval time.Duration) SignatureValidator { +func NewSignatureValidatorFromJSONWebKeySetFile(path string) SignatureValidator { // Hmm. I don't want us to first read the file once, to initialize this, and then read the file periodically to update it. // However, I also don't want us to initialize this in a state that isn't ready for use. validator := NewForwardingSignatureValidator(nil) @@ -124,7 +123,7 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, refreshInterval tim // TODO: Run this as part of the program.Group, so that it gets // cleaned up upon shutdown. go func() { - t := time.NewTicker(refreshInterval) + t := time.NewTicker(300 * time.Second) for range t.C { jwksJSON, err := os.ReadFile(path) if err != nil { diff --git a/pkg/proto/configuration/jwt/jwt.pb.go b/pkg/proto/configuration/jwt/jwt.pb.go old mode 100644 new mode 100755 index 1f671712..ff41335e --- a/pkg/proto/configuration/jwt/jwt.pb.go +++ b/pkg/proto/configuration/jwt/jwt.pb.go @@ -10,7 +10,7 @@ import ( eviction "github.com/buildbarn/bb-storage/pkg/proto/configuration/eviction" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" + _ "google.golang.org/protobuf/types/known/durationpb" structpb "google.golang.org/protobuf/types/known/structpb" reflect "reflect" sync "sync" @@ -31,7 +31,7 @@ type AuthorizationHeaderParserConfiguration struct { // Types that are assignable to Jwks: // // *AuthorizationHeaderParserConfiguration_JwksInline - // *AuthorizationHeaderParserConfiguration_JwksFile_ + // *AuthorizationHeaderParserConfiguration_JwksFile Jwks isAuthorizationHeaderParserConfiguration_Jwks `protobuf_oneof:"jwks"` MaximumCacheSize int32 `protobuf:"varint,3,opt,name=maximum_cache_size,json=maximumCacheSize,proto3" json:"maximum_cache_size,omitempty"` CacheReplacementPolicy eviction.CacheReplacementPolicy `protobuf:"varint,4,opt,name=cache_replacement_policy,json=cacheReplacementPolicy,proto3,enum=buildbarn.configuration.eviction.CacheReplacementPolicy" json:"cache_replacement_policy,omitempty"` @@ -85,11 +85,11 @@ func (x *AuthorizationHeaderParserConfiguration) GetJwksInline() *structpb.Struc return nil } -func (x *AuthorizationHeaderParserConfiguration) GetJwksFile() *AuthorizationHeaderParserConfiguration_JwksFile { - if x, ok := x.GetJwks().(*AuthorizationHeaderParserConfiguration_JwksFile_); ok { +func (x *AuthorizationHeaderParserConfiguration) GetJwksFile() string { + if x, ok := x.GetJwks().(*AuthorizationHeaderParserConfiguration_JwksFile); ok { return x.JwksFile } - return nil + return "" } func (x *AuthorizationHeaderParserConfiguration) GetMaximumCacheSize() int32 { @@ -128,69 +128,14 @@ type AuthorizationHeaderParserConfiguration_JwksInline struct { JwksInline *structpb.Struct `protobuf:"bytes,7,opt,name=jwks_inline,json=jwksInline,proto3,oneof"` } -type AuthorizationHeaderParserConfiguration_JwksFile_ struct { - JwksFile *AuthorizationHeaderParserConfiguration_JwksFile `protobuf:"bytes,8,opt,name=jwks_file,json=jwksFile,proto3,oneof"` -} - -func (*AuthorizationHeaderParserConfiguration_JwksInline) isAuthorizationHeaderParserConfiguration_Jwks() { -} - -func (*AuthorizationHeaderParserConfiguration_JwksFile_) isAuthorizationHeaderParserConfiguration_Jwks() { -} - type AuthorizationHeaderParserConfiguration_JwksFile struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - FilePath string `protobuf:"bytes,1,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` - RefreshInterval *durationpb.Duration `protobuf:"bytes,2,opt,name=refresh_interval,json=refreshInterval,proto3" json:"refresh_interval,omitempty"` -} - -func (x *AuthorizationHeaderParserConfiguration_JwksFile) Reset() { - *x = AuthorizationHeaderParserConfiguration_JwksFile{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AuthorizationHeaderParserConfiguration_JwksFile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AuthorizationHeaderParserConfiguration_JwksFile) ProtoMessage() {} - -func (x *AuthorizationHeaderParserConfiguration_JwksFile) ProtoReflect() protoreflect.Message { - mi := &file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1] - 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) + JwksFile string `protobuf:"bytes,8,opt,name=jwks_file,json=jwksFile,proto3,oneof"` } -// Deprecated: Use AuthorizationHeaderParserConfiguration_JwksFile.ProtoReflect.Descriptor instead. -func (*AuthorizationHeaderParserConfiguration_JwksFile) Descriptor() ([]byte, []int) { - return file_pkg_proto_configuration_jwt_jwt_proto_rawDescGZIP(), []int{0, 0} -} - -func (x *AuthorizationHeaderParserConfiguration_JwksFile) GetFilePath() string { - if x != nil { - return x.FilePath - } - return "" +func (*AuthorizationHeaderParserConfiguration_JwksInline) isAuthorizationHeaderParserConfiguration_Jwks() { } -func (x *AuthorizationHeaderParserConfiguration_JwksFile) GetRefreshInterval() *durationpb.Duration { - if x != nil { - return x.RefreshInterval - } - return nil +func (*AuthorizationHeaderParserConfiguration_JwksFile) isAuthorizationHeaderParserConfiguration_Jwks() { } var File_pkg_proto_configuration_jwt_jwt_proto protoreflect.FileDescriptor @@ -207,53 +152,41 @@ var file_pkg_proto_configuration_jwt_jwt_proto_rawDesc = []byte{ 0x74, 0x6f, 0x1a, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x05, 0x0a, 0x26, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x6f, 0x74, 0x6f, 0x22, 0xe3, 0x03, 0x0a, 0x26, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0a, - 0x6a, 0x77, 0x6b, 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x6b, 0x0a, 0x09, 0x6a, 0x77, - 0x6b, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x4c, 0x2e, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x6a, 0x77, 0x74, 0x2e, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, - 0x61, 0x72, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6a, - 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x69, 0x6d, - 0x75, 0x6d, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x61, 0x63, 0x68, - 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x72, 0x0a, 0x18, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x72, - 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, - 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, - 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x52, 0x16, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x25, 0x63, 0x6c, 0x61, - 0x69, 0x6d, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6a, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, - 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x55, 0x0a, 0x27, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x24, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x1a, 0x6d, 0x0a, 0x08, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, - 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x44, 0x0a, 0x10, - 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, - 0x61, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, - 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2f, 0x62, - 0x62, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2f, 0x6a, 0x77, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6a, 0x77, 0x6b, 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x6a, 0x77, + 0x6b, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, + 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x43, 0x61, + 0x63, 0x68, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x72, 0x0a, 0x18, 0x63, 0x61, 0x63, 0x68, 0x65, + 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x38, 0x2e, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x62, 0x61, 0x72, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x65, 0x76, 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x63, + 0x68, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x16, 0x63, 0x61, 0x63, 0x68, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x51, 0x0a, 0x25, 0x63, + 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x22, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x55, + 0x0a, 0x27, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, + 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x24, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4a, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x70, 0x72, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x4a, 0x04, 0x08, + 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x62, 0x61, 0x72, + 0x6e, 0x2f, 0x62, 0x62, 0x2d, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x77, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -268,24 +201,20 @@ func file_pkg_proto_configuration_jwt_jwt_proto_rawDescGZIP() []byte { return file_pkg_proto_configuration_jwt_jwt_proto_rawDescData } -var file_pkg_proto_configuration_jwt_jwt_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_pkg_proto_configuration_jwt_jwt_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_pkg_proto_configuration_jwt_jwt_proto_goTypes = []interface{}{ - (*AuthorizationHeaderParserConfiguration)(nil), // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration - (*AuthorizationHeaderParserConfiguration_JwksFile)(nil), // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile - (*structpb.Struct)(nil), // 2: google.protobuf.Struct - (eviction.CacheReplacementPolicy)(0), // 3: buildbarn.configuration.eviction.CacheReplacementPolicy - (*durationpb.Duration)(nil), // 4: google.protobuf.Duration + (*AuthorizationHeaderParserConfiguration)(nil), // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration + (*structpb.Struct)(nil), // 1: google.protobuf.Struct + (eviction.CacheReplacementPolicy)(0), // 2: buildbarn.configuration.eviction.CacheReplacementPolicy } var file_pkg_proto_configuration_jwt_jwt_proto_depIdxs = []int32{ - 2, // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_inline:type_name -> google.protobuf.Struct - 1, // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_file:type_name -> buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile - 3, // 2: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.cache_replacement_policy:type_name -> buildbarn.configuration.eviction.CacheReplacementPolicy - 4, // 3: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.JwksFile.refresh_interval:type_name -> google.protobuf.Duration - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 1, // 0: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.jwks_inline:type_name -> google.protobuf.Struct + 2, // 1: buildbarn.configuration.jwt.AuthorizationHeaderParserConfiguration.cache_replacement_policy:type_name -> buildbarn.configuration.eviction.CacheReplacementPolicy + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_pkg_proto_configuration_jwt_jwt_proto_init() } @@ -306,22 +235,10 @@ func file_pkg_proto_configuration_jwt_jwt_proto_init() { return nil } } - file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AuthorizationHeaderParserConfiguration_JwksFile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } file_pkg_proto_configuration_jwt_jwt_proto_msgTypes[0].OneofWrappers = []interface{}{ (*AuthorizationHeaderParserConfiguration_JwksInline)(nil), - (*AuthorizationHeaderParserConfiguration_JwksFile_)(nil), + (*AuthorizationHeaderParserConfiguration_JwksFile)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -329,7 +246,7 @@ func file_pkg_proto_configuration_jwt_jwt_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pkg_proto_configuration_jwt_jwt_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 1, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/configuration/jwt/jwt.proto b/pkg/proto/configuration/jwt/jwt.proto index 31b8c233..472cd8d1 100644 --- a/pkg/proto/configuration/jwt/jwt.proto +++ b/pkg/proto/configuration/jwt/jwt.proto @@ -15,14 +15,6 @@ message AuthorizationHeaderParserConfiguration { // Was 'public_key'. Replaced by 'jwks_inline'. reserved 2; - message JwksFile { - // Path to JWKS file. - string file_path = 1; - - // Interval at which to refresh JWKS file. - google.protobuf.Duration refresh_interval = 2; - } - oneof jwks { // A JSON Web Key Set (JWKS) as described in RFC 7517, chapter 5, // containing one or more public keys against which tokens signatures @@ -36,9 +28,9 @@ message AuthorizationHeaderParserConfiguration { // - https://russelldavies.github.io/jwk-creator/ google.protobuf.Struct jwks_inline = 7; - // Similar to jwks_inline, but reads the JWKS from a file, with a - // refresh interval. - JwksFile jwks_file = 8; + // Similar to jwks_inline, but reads the JWKS from a file. + // The file will be automatically reloaded every 300 seconds. + string jwks_file = 8; } // Maximum number of validated tokens to cache in memory. This speeds From a4436e439591263f594cf579fd40085f70f2d037 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sun, 8 Oct 2023 13:06:35 +0200 Subject: [PATCH 05/25] Make ForwardingSignatureValidator a public type, and have NewForwardingSignatureValidator return that --- pkg/jwt/configuration.go | 2 +- pkg/jwt/forwarding_signature_validator.go | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index b9b50a3e..93d7ffbf 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -143,7 +143,7 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string) SignatureValidator continue } - (validator.(*forwardingSignatureValidator)).Replace(signatureValidator) + validator.Replace(signatureValidator) } }() diff --git a/pkg/jwt/forwarding_signature_validator.go b/pkg/jwt/forwarding_signature_validator.go index f6d59082..aa3efea9 100644 --- a/pkg/jwt/forwarding_signature_validator.go +++ b/pkg/jwt/forwarding_signature_validator.go @@ -4,23 +4,25 @@ import ( "sync/atomic" ) -type forwardingSignatureValidator struct { +type ForwardingSignatureValidator struct { validator atomic.Pointer[SignatureValidator] } // NewForwardingSignatureValidator creates a SignatureValidator that simply forwards // requests to another SignatureValidator. -func NewForwardingSignatureValidator(validator SignatureValidator) SignatureValidator { - sv := &forwardingSignatureValidator{} +// This returns a pointer to the new ForwardingSignatureValidator, so as not to +// copy the atomic.Pointer. +func NewForwardingSignatureValidator(validator SignatureValidator) *ForwardingSignatureValidator { + sv := ForwardingSignatureValidator{} sv.validator.Store(&validator) - return sv + return &sv } -func (sv *forwardingSignatureValidator) Replace(validator SignatureValidator) { +func (sv *ForwardingSignatureValidator) Replace(validator SignatureValidator) { sv.validator.Store(&validator) } -func (sv *forwardingSignatureValidator) ValidateSignature(algorithm string, keyID *string, headerAndPayload string, signature []byte) bool { +func (sv *ForwardingSignatureValidator) ValidateSignature(algorithm string, keyID *string, headerAndPayload string, signature []byte) bool { return (*sv.validator.Load()).ValidateSignature(algorithm, keyID, headerAndPayload, signature) } From 0de767a83be477429c217374f845188c696a2079 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sun, 8 Oct 2023 13:23:22 +0200 Subject: [PATCH 06/25] Init the ForwardingSignatureValidator with a SignatureValidator on initial creation --- pkg/jwt/configuration.go | 53 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 93d7ffbf..ba8cbef0 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -115,37 +115,50 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature return NewDemultiplexingSignatureValidator(namedSignatureValidators, allSignatureValidators), nil } -func NewSignatureValidatorFromJSONWebKeySetFile(path string) SignatureValidator { - // Hmm. I don't want us to first read the file once, to initialize this, and then read the file periodically to update it. - // However, I also don't want us to initialize this in a state that isn't ready for use. - validator := NewForwardingSignatureValidator(nil) +// NewSignatureValidatorFromJSONWebKeySetFile creates a new +// SignatureValidator capable of validating JWTs matching keys contained +// in a JSON Web Key Set read from a file. The content of the file is +// periodically refreshed. +func NewSignatureValidatorFromJSONWebKeySetFile(path string) (SignatureValidator, error) { + internalValidator, err := getJwksFromFile(path) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %s", path) + } + forwardingValidator := NewForwardingSignatureValidator(internalValidator) // TODO: Run this as part of the program.Group, so that it gets // cleaned up upon shutdown. go func() { t := time.NewTicker(300 * time.Second) for range t.C { - jwksJSON, err := os.ReadFile(path) - if err != nil { - log.Printf("Failed to reload JWKS file: %v", err) - continue - } - - var jwks jose.JSONWebKeySet - if err := json.Unmarshal(jwksJSON, &jwks); err != nil { - log.Printf("Failed to reload JWKS file: %v", err) - continue - } - - signatureValidator, err := NewSignatureValidatorFromJSONWebKeySet(&jwks) + internalValidator, err := getJwksFromFile(path) if err != nil { - log.Printf("Failed to create SignatureValidator for JWKS file: %v", err) + log.Printf("Failed to get JWKS content from file: %v", err) continue } - validator.Replace(signatureValidator) + forwardingValidator.Replace(internalValidator) } }() - return validator + return forwardingValidator, nil +} + +func getJwksFromFile(path string) (SignatureValidator, error) { + jwksJSON, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var jwks jose.JSONWebKeySet + if err := json.Unmarshal(jwksJSON, &jwks); err != nil { + return nil, err + } + + signatureValidator, err := NewSignatureValidatorFromJSONWebKeySet(&jwks) + if err != nil { + return nil, err + } + + return signatureValidator, nil } From 76aa95b3ba6c02dc653980cafba4c3903902f88b Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sun, 8 Oct 2023 13:42:28 +0200 Subject: [PATCH 07/25] Run JWKS refresh as part of the program group --- pkg/grpc/authenticator.go | 9 +++++---- pkg/grpc/server.go | 2 +- pkg/http/authenticator.go | 9 +++++---- pkg/http/server.go | 2 +- pkg/jwt/BUILD.bazel | 1 + pkg/jwt/configuration.go | 41 +++++++++++++++++++++++---------------- 6 files changed, 37 insertions(+), 27 deletions(-) diff --git a/pkg/grpc/authenticator.go b/pkg/grpc/authenticator.go index d09752ef..eadc0448 100644 --- a/pkg/grpc/authenticator.go +++ b/pkg/grpc/authenticator.go @@ -7,6 +7,7 @@ import ( "github.com/buildbarn/bb-storage/pkg/auth" "github.com/buildbarn/bb-storage/pkg/clock" "github.com/buildbarn/bb-storage/pkg/jwt" + "github.com/buildbarn/bb-storage/pkg/program" configuration "github.com/buildbarn/bb-storage/pkg/proto/configuration/grpc" "github.com/buildbarn/bb-storage/pkg/util" "github.com/jmespath/go-jmespath" @@ -24,7 +25,7 @@ type Authenticator interface { // NewAuthenticatorFromConfiguration creates a tree of Authenticator // objects based on a configuration file. -func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolicy) (Authenticator, bool, error) { +func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolicy, group program.Group) (Authenticator, bool, error) { if policy == nil { return nil, false, status.Error(codes.InvalidArgument, "Authentication policy not specified") } @@ -39,7 +40,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic children := make([]Authenticator, 0, len(policyKind.Any.Policies)) needsPeerTransportCredentials := false for _, childConfiguration := range policyKind.Any.Policies { - child, childNeedsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(childConfiguration) + child, childNeedsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(childConfiguration, group) if err != nil { return nil, false, err } @@ -51,7 +52,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic children := make([]Authenticator, 0, len(policyKind.All.Policies)) needsPeerTransportCredentials := false for _, childConfiguration := range policyKind.All.Policies { - child, childNeedsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(childConfiguration) + child, childNeedsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(childConfiguration, group) if err != nil { return nil, false, err } @@ -81,7 +82,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic metadataExtractor, ), false, nil case *configuration.AuthenticationPolicy_Jwt: - authorizationHeaderParser, err := jwt.NewAuthorizationHeaderParserFromConfiguration(policyKind.Jwt) + authorizationHeaderParser, err := jwt.NewAuthorizationHeaderParserFromConfiguration(policyKind.Jwt, group) if err != nil { return nil, false, util.StatusWrap(err, "Failed to create authorization header parser for JWT authentication policy") } diff --git a/pkg/grpc/server.go b/pkg/grpc/server.go index 510e0635..eda6073e 100644 --- a/pkg/grpc/server.go +++ b/pkg/grpc/server.go @@ -36,7 +36,7 @@ func init() { func NewServersFromConfigurationAndServe(configurations []*configuration.ServerConfiguration, registrationFunc func(grpc.ServiceRegistrar), group program.Group) error { for _, configuration := range configurations { // Create an authenticator for requests. - authenticator, needsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy) + authenticator, needsPeerTransportCredentials, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy, group) if err != nil { return err } diff --git a/pkg/http/authenticator.go b/pkg/http/authenticator.go index 535cfa1a..6db0f7f5 100644 --- a/pkg/http/authenticator.go +++ b/pkg/http/authenticator.go @@ -10,6 +10,7 @@ import ( "github.com/buildbarn/bb-storage/pkg/auth" "github.com/buildbarn/bb-storage/pkg/clock" "github.com/buildbarn/bb-storage/pkg/jwt" + "github.com/buildbarn/bb-storage/pkg/program" configuration "github.com/buildbarn/bb-storage/pkg/proto/configuration/http" "github.com/buildbarn/bb-storage/pkg/random" "github.com/buildbarn/bb-storage/pkg/util" @@ -30,7 +31,7 @@ type Authenticator interface { // NewAuthenticatorFromConfiguration creates a tree of Authenticator // objects based on a configuration file. -func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolicy) (Authenticator, error) { +func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolicy, group program.Group) (Authenticator, error) { if policy == nil { return nil, status.Error(codes.InvalidArgument, "Authentication policy not specified") } @@ -44,7 +45,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic case *configuration.AuthenticationPolicy_Any: children := make([]Authenticator, 0, len(policyKind.Any.Policies)) for _, childConfiguration := range policyKind.Any.Policies { - child, err := NewAuthenticatorFromConfiguration(childConfiguration) + child, err := NewAuthenticatorFromConfiguration(childConfiguration, group) if err != nil { return nil, err } @@ -54,7 +55,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic case *configuration.AuthenticationPolicy_Deny: return NewDenyAuthenticator(policyKind.Deny), nil case *configuration.AuthenticationPolicy_Jwt: - authorizationHeaderParser, err := jwt.NewAuthorizationHeaderParserFromConfiguration(policyKind.Jwt) + authorizationHeaderParser, err := jwt.NewAuthorizationHeaderParserFromConfiguration(policyKind.Jwt, group) if err != nil { return nil, util.StatusWrap(err, "Failed to create authorization header parser for JWT authentication policy") } @@ -118,7 +119,7 @@ func NewAuthenticatorFromConfiguration(policy *configuration.AuthenticationPolic cookieAEAD, clock.SystemClock) case *configuration.AuthenticationPolicy_AcceptHeader: - base, err := NewAuthenticatorFromConfiguration(policyKind.AcceptHeader.Policy) + base, err := NewAuthenticatorFromConfiguration(policyKind.AcceptHeader.Policy, group) if err != nil { return nil, err } diff --git a/pkg/http/server.go b/pkg/http/server.go index a6adabe3..aadc2578 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -15,7 +15,7 @@ import ( // canceled. func NewServersFromConfigurationAndServe(configurations []*configuration.ServerConfiguration, handler http.Handler, group program.Group) error { for _, configuration := range configurations { - authenticator, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy) + authenticator, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy, group) if err != nil { return err } diff --git a/pkg/jwt/BUILD.bazel b/pkg/jwt/BUILD.bazel index 71a16ce6..f3435060 100644 --- a/pkg/jwt/BUILD.bazel +++ b/pkg/jwt/BUILD.bazel @@ -22,6 +22,7 @@ go_library( "//pkg/auth", "//pkg/clock", "//pkg/eviction", + "//pkg/program", "//pkg/proto/configuration/jwt", "//pkg/random", "//pkg/util", diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index ba8cbef0..f8f7383f 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -1,17 +1,18 @@ package jwt import ( + "context" "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" "encoding/json" - "log" "os" "reflect" "time" "github.com/buildbarn/bb-storage/pkg/clock" "github.com/buildbarn/bb-storage/pkg/eviction" + "github.com/buildbarn/bb-storage/pkg/program" configuration "github.com/buildbarn/bb-storage/pkg/proto/configuration/jwt" "github.com/buildbarn/bb-storage/pkg/util" jose "github.com/go-jose/go-jose/v3" @@ -25,7 +26,7 @@ import ( // NewAuthorizationHeaderParserFromConfiguration creates a new HTTP // "Authorization" header parser based on options stored in a // configuration file. -func NewAuthorizationHeaderParserFromConfiguration(config *configuration.AuthorizationHeaderParserConfiguration) (*AuthorizationHeaderParser, error) { +func NewAuthorizationHeaderParserFromConfiguration(config *configuration.AuthorizationHeaderParserConfiguration, group program.Group) (*AuthorizationHeaderParser, error) { var signatureValidator SignatureValidator switch key := config.Jwks.(type) { @@ -43,7 +44,11 @@ func NewAuthorizationHeaderParserFromConfiguration(config *configuration.Authori return nil, err } case *configuration.AuthorizationHeaderParserConfiguration_JwksFile: - signatureValidator = NewSignatureValidatorFromJSONWebKeySetFile(key.JwksFile) + var err error + signatureValidator, err = NewSignatureValidatorFromJSONWebKeySetFile(key.JwksFile, group) + if err != nil { + return nil, err + } default: return nil, status.Error(codes.InvalidArgument, "No key type provided") } @@ -119,27 +124,29 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature // SignatureValidator capable of validating JWTs matching keys contained // in a JSON Web Key Set read from a file. The content of the file is // periodically refreshed. -func NewSignatureValidatorFromJSONWebKeySetFile(path string) (SignatureValidator, error) { +func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group) (SignatureValidator, error) { internalValidator, err := getJwksFromFile(path) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %s", path) } forwardingValidator := NewForwardingSignatureValidator(internalValidator) - // TODO: Run this as part of the program.Group, so that it gets - // cleaned up upon shutdown. - go func() { + group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { t := time.NewTicker(300 * time.Second) - for range t.C { - internalValidator, err := getJwksFromFile(path) - if err != nil { - log.Printf("Failed to get JWKS content from file: %v", err) - continue - } - - forwardingValidator.Replace(internalValidator) - } - }() + for { + select { + case <-t.C: + internalValidator, err := getJwksFromFile(path) + if err != nil { + return err + } + + forwardingValidator.Replace(internalValidator) + case <-ctx.Done(): + return util.StatusFromContext(ctx) + } + } + }) return forwardingValidator, nil } From 516fb4ef9f1c25718f48b1caafaa0d7ad55d661d Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sun, 8 Oct 2023 14:00:00 +0200 Subject: [PATCH 08/25] Lint --- pkg/jwt/configuration.go | 10 +++++----- pkg/proto/configuration/jwt/jwt.pb.go | 0 2 files changed, 5 insertions(+), 5 deletions(-) mode change 100755 => 100644 pkg/proto/configuration/jwt/jwt.pb.go diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index f8f7383f..ef482c99 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -134,18 +134,18 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { t := time.NewTicker(300 * time.Second) for { - select { - case <-t.C: + select { + case <-t.C: internalValidator, err := getJwksFromFile(path) if err != nil { return err } forwardingValidator.Replace(internalValidator) - case <-ctx.Done(): + case <-ctx.Done(): return util.StatusFromContext(ctx) - } - } + } + } }) return forwardingValidator, nil diff --git a/pkg/proto/configuration/jwt/jwt.pb.go b/pkg/proto/configuration/jwt/jwt.pb.go old mode 100755 new mode 100644 From 50d17108c0edae3499c0f87c8501cd17d5cebd19 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 13:55:53 +0200 Subject: [PATCH 09/25] Defer t.Stop() --- pkg/jwt/configuration.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index ef482c99..4385bab7 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -133,6 +133,8 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { t := time.NewTicker(300 * time.Second) + defer t.Stop() + for { select { case <-t.C: From b519c3107c82ca8ad5221a418dc16df48a6f9757 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 13:58:56 +0200 Subject: [PATCH 10/25] Log errors from getJwksFromFile and continue --- pkg/jwt/configuration.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 4385bab7..03e4bf5a 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -6,6 +6,7 @@ import ( "crypto/ed25519" "crypto/rsa" "encoding/json" + "log" "os" "reflect" "time" @@ -140,10 +141,11 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group case <-t.C: internalValidator, err := getJwksFromFile(path) if err != nil { - return err + log.Printf("Failed to read JWKS content from file at %s: %s", path, err) + continue } - forwardingValidator.Replace(internalValidator) + case <-ctx.Done(): return util.StatusFromContext(ctx) } From c36da1ce3effd12c6d88514ec6b69069c8ee368a Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 13:59:27 +0200 Subject: [PATCH 11/25] Simplify return in getJwksFromFile --- pkg/jwt/configuration.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 03e4bf5a..48048252 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -166,10 +166,5 @@ func getJwksFromFile(path string) (SignatureValidator, error) { return nil, err } - signatureValidator, err := NewSignatureValidatorFromJSONWebKeySet(&jwks) - if err != nil { - return nil, err - } - - return signatureValidator, nil + return NewSignatureValidatorFromJSONWebKeySet(&jwks) } From ba426d06d5562c4eec4fb090a982aa4205f76ac4 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 14:05:08 +0200 Subject: [PATCH 12/25] Use json.NewDecoder --- pkg/jwt/configuration.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 48048252..5644a09e 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -156,13 +156,14 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group } func getJwksFromFile(path string) (SignatureValidator, error) { - jwksJSON, err := os.ReadFile(path) + r, err := os.Open(path) if err != nil { return nil, err } var jwks jose.JSONWebKeySet - if err := json.Unmarshal(jwksJSON, &jwks); err != nil { + err = json.NewDecoder(r).Decode(&jwks) + if err != nil { return nil, err } From 7af27415fe7668e8205c04c31d0bfce592a1da73 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 14:10:19 +0200 Subject: [PATCH 13/25] Reorder proto comments --- pkg/proto/configuration/jwt/jwt.proto | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/proto/configuration/jwt/jwt.proto b/pkg/proto/configuration/jwt/jwt.proto index 472cd8d1..be6c4d26 100644 --- a/pkg/proto/configuration/jwt/jwt.proto +++ b/pkg/proto/configuration/jwt/jwt.proto @@ -15,20 +15,21 @@ message AuthorizationHeaderParserConfiguration { // Was 'public_key'. Replaced by 'jwks_inline'. reserved 2; + // A JSON Web Key Set (JWKS) as described in RFC 7517, chapter 5, + // containing one or more public keys against which tokens signatures + // are validated. + // + // Public keys encoded in DER or PEM format can be converted to a JSON + // Web Key Set using one of the following tools: + // + // - https://smallstep.com/docs/step-cli/reference/crypto/key/format/ + // - https://irrte.ch/jwt-js-decode/pem2jwk.html + // - https://russelldavies.github.io/jwk-creator/ oneof jwks { - // A JSON Web Key Set (JWKS) as described in RFC 7517, chapter 5, - // containing one or more public keys against which tokens signatures - // are validated. - // - // Public keys encoded in DER or PEM format can be converted to a JSON - // Web Key Set using one of the following tools: - // - // - https://smallstep.com/docs/step-cli/reference/crypto/key/format/ - // - https://irrte.ch/jwt-js-decode/pem2jwk.html - // - https://russelldavies.github.io/jwk-creator/ + // JWKS stored inline in the configuration. google.protobuf.Struct jwks_inline = 7; - // Similar to jwks_inline, but reads the JWKS from a file. + // JWKS stored in a separate file. // The file will be automatically reloaded every 300 seconds. string jwks_file = 8; } From 82ecff8ab7cffdab2d1ea9475ad34887863ba7a3 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 13 Oct 2023 14:39:19 +0200 Subject: [PATCH 14/25] Use dependenciesGroup in http/server.go --- pkg/global/apply_configuration.go | 14 +++------- pkg/http/server.go | 46 ++++++++++++++++--------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/pkg/global/apply_configuration.go b/pkg/global/apply_configuration.go index a00b49df..208d3d45 100644 --- a/pkg/global/apply_configuration.go +++ b/pkg/global/apply_configuration.go @@ -70,16 +70,10 @@ func (ls *LifecycleState) MarkReadyAndWait(group program.Group) { router.Handle("/active_spans", httpHandler) } - group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { - if err := bb_http.NewServersFromConfigurationAndServe( - ls.config.HttpServers, - bb_http.NewMetricsHandler(router, "Diagnostics"), - group, - ); err != nil { - return util.StatusWrap(err, "Failed to launch diagnostics HTTP server") - } - return nil - }) + bb_http.NewServersFromConfigurationAndServe( + ls.config.HttpServers, + bb_http.NewMetricsHandler(router, "Diagnostics"), + group) } } diff --git a/pkg/http/server.go b/pkg/http/server.go index aadc2578..b259f6ea 100644 --- a/pkg/http/server.go +++ b/pkg/http/server.go @@ -13,29 +13,31 @@ import ( // program.Group, based on a configuration message. The web servers are // automatically terminated if the context associated with the group is // canceled. -func NewServersFromConfigurationAndServe(configurations []*configuration.ServerConfiguration, handler http.Handler, group program.Group) error { - for _, configuration := range configurations { - authenticator, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy, group) - if err != nil { - return err - } - authenticatedHandler := NewAuthenticatingHandler(handler, authenticator) - for _, listenAddress := range configuration.ListenAddresses { - server := http.Server{ - Addr: listenAddress, - Handler: authenticatedHandler, +func NewServersFromConfigurationAndServe(configurations []*configuration.ServerConfiguration, handler http.Handler, group program.Group) { + group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { + for _, configuration := range configurations { + authenticator, err := NewAuthenticatorFromConfiguration(configuration.AuthenticationPolicy, dependenciesGroup) + if err != nil { + return err } - group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { - <-ctx.Done() - return server.Close() - }) - group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { - if err := server.ListenAndServe(); err != http.ErrServerClosed { - return util.StatusWrapf(err, "Failed to launch HTTP server %#v", server.Addr) + authenticatedHandler := NewAuthenticatingHandler(handler, authenticator) + for _, listenAddress := range configuration.ListenAddresses { + server := http.Server{ + Addr: listenAddress, + Handler: authenticatedHandler, } - return nil - }) + group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { + <-ctx.Done() + return server.Close() + }) + group.Go(func(ctx context.Context, siblingsGroup, dependenciesGroup program.Group) error { + if err := server.ListenAndServe(); err != http.ErrServerClosed { + return util.StatusWrapf(err, "Failed to launch HTTP server %#v", server.Addr) + } + return nil + }) + } } - } - return nil + return nil + }) } From 14c2f8fde05c43f56a391ea613fdc8bd0fa58474 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Tue, 17 Oct 2023 11:14:53 +0200 Subject: [PATCH 15/25] Close file --- pkg/jwt/configuration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 5644a09e..e16eb5d7 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -160,6 +160,7 @@ func getJwksFromFile(path string) (SignatureValidator, error) { if err != nil { return nil, err } + defer r.Close() var jwks jose.JSONWebKeySet err = json.NewDecoder(r).Decode(&jwks) From dbf724112357242a87c7657b813b7efb1790d26d Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Tue, 17 Oct 2023 11:15:32 +0200 Subject: [PATCH 16/25] Casing --- pkg/jwt/configuration.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index e16eb5d7..22b62f10 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -126,7 +126,7 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature // in a JSON Web Key Set read from a file. The content of the file is // periodically refreshed. func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group) (SignatureValidator, error) { - internalValidator, err := getJwksFromFile(path) + internalValidator, err := getJWKSFromFile(path) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %s", path) } @@ -139,7 +139,7 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group for { select { case <-t.C: - internalValidator, err := getJwksFromFile(path) + internalValidator, err := getJWKSFromFile(path) if err != nil { log.Printf("Failed to read JWKS content from file at %s: %s", path, err) continue @@ -155,7 +155,7 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group return forwardingValidator, nil } -func getJwksFromFile(path string) (SignatureValidator, error) { +func getJWKSFromFile(path string) (SignatureValidator, error) { r, err := os.Open(path) if err != nil { return nil, err From 727655a58086010eae0e523615eb18b2bac82f3b Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Tue, 17 Oct 2023 11:17:08 +0200 Subject: [PATCH 17/25] Use %#v to quote paths --- pkg/jwt/configuration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 22b62f10..9b233011 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -128,7 +128,7 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group) (SignatureValidator, error) { internalValidator, err := getJWKSFromFile(path) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %s", path) + return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %#v", path) } forwardingValidator := NewForwardingSignatureValidator(internalValidator) @@ -141,7 +141,7 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group case <-t.C: internalValidator, err := getJWKSFromFile(path) if err != nil { - log.Printf("Failed to read JWKS content from file at %s: %s", path, err) + log.Printf("Failed to read JWKS content from file at %#v: %s", path, err) continue } forwardingValidator.Replace(internalValidator) From 23879f2d364eb09a7ec9dbd91e60d41b2339c353 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Tue, 17 Oct 2023 11:18:35 +0200 Subject: [PATCH 18/25] Use util.StatusWrapf --- pkg/jwt/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 9b233011..5e114dc9 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -128,7 +128,7 @@ func NewSignatureValidatorFromJSONWebKeySet(jwks *jose.JSONWebKeySet) (Signature func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group) (SignatureValidator, error) { internalValidator, err := getJWKSFromFile(path) if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "Unable to read JWKS content from file at %#v", path) + return nil, util.StatusWrapf(err, "Unable to read JWKS content from file at %#v", path) } forwardingValidator := NewForwardingSignatureValidator(internalValidator) From 74cf1f7b91e09a68ec1386b4fef2c6d1b233e8fc Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Tue, 17 Oct 2023 11:20:16 +0200 Subject: [PATCH 19/25] Document ForwardingSignatureValidator --- pkg/jwt/forwarding_signature_validator.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/jwt/forwarding_signature_validator.go b/pkg/jwt/forwarding_signature_validator.go index aa3efea9..90d5c7db 100644 --- a/pkg/jwt/forwarding_signature_validator.go +++ b/pkg/jwt/forwarding_signature_validator.go @@ -4,6 +4,9 @@ import ( "sync/atomic" ) +// ForwardingSignatureValidator wraps another SignatureValidator. It is +// used when the underlying SignatureValidator needs to be replaced at +// runtime. type ForwardingSignatureValidator struct { validator atomic.Pointer[SignatureValidator] } From 9c0ff72426f8540dab1e66aee35a0ed986cd6e54 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 27 Oct 2023 12:40:18 +0200 Subject: [PATCH 20/25] Revert "Use json.NewDecoder" This reverts commit ba426d06d5562c4eec4fb090a982aa4205f76ac4. --- pkg/jwt/configuration.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 5e114dc9..c148137a 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -163,8 +163,7 @@ func getJWKSFromFile(path string) (SignatureValidator, error) { defer r.Close() var jwks jose.JSONWebKeySet - err = json.NewDecoder(r).Decode(&jwks) - if err != nil { + if err := json.Unmarshal(jwksJSON, &jwks); err != nil { return nil, err } From ecbb54ba822bc6ccdc475e1bd44d7313f0302e87 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 27 Oct 2023 12:51:57 +0200 Subject: [PATCH 21/25] Use io.ReadAll() --- pkg/jwt/configuration.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index c148137a..b701cda2 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -6,6 +6,7 @@ import ( "crypto/ed25519" "crypto/rsa" "encoding/json" + "io" "log" "os" "reflect" @@ -162,8 +163,13 @@ func getJWKSFromFile(path string) (SignatureValidator, error) { } defer r.Close() + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + var jwks jose.JSONWebKeySet - if err := json.Unmarshal(jwksJSON, &jwks); err != nil { + if err := json.Unmarshal(data, &jwks); err != nil { return nil, err } From 3f1266fad795c63675c8a5a898fdc25c7dddb56c Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 27 Oct 2023 12:54:55 +0200 Subject: [PATCH 22/25] Document methods --- pkg/jwt/forwarding_signature_validator.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/jwt/forwarding_signature_validator.go b/pkg/jwt/forwarding_signature_validator.go index 90d5c7db..a846fbdb 100644 --- a/pkg/jwt/forwarding_signature_validator.go +++ b/pkg/jwt/forwarding_signature_validator.go @@ -22,10 +22,12 @@ func NewForwardingSignatureValidator(validator SignatureValidator) *ForwardingSi return &sv } +// Replace the registered SignatureValidator func (sv *ForwardingSignatureValidator) Replace(validator SignatureValidator) { sv.validator.Store(&validator) } +// Validate a signature using the registered SignatureValidator func (sv *ForwardingSignatureValidator) ValidateSignature(algorithm string, keyID *string, headerAndPayload string, signature []byte) bool { return (*sv.validator.Load()).ValidateSignature(algorithm, keyID, headerAndPayload, signature) } From 2d6216954d5d7a6fb0a6b4c48b62a3406566cbae Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Fri, 27 Oct 2023 23:12:27 +0200 Subject: [PATCH 23/25] Fix docstrings --- pkg/jwt/forwarding_signature_validator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/jwt/forwarding_signature_validator.go b/pkg/jwt/forwarding_signature_validator.go index a846fbdb..85132b70 100644 --- a/pkg/jwt/forwarding_signature_validator.go +++ b/pkg/jwt/forwarding_signature_validator.go @@ -22,12 +22,12 @@ func NewForwardingSignatureValidator(validator SignatureValidator) *ForwardingSi return &sv } -// Replace the registered SignatureValidator +// Replace replaces the registered SignatureValidator func (sv *ForwardingSignatureValidator) Replace(validator SignatureValidator) { sv.validator.Store(&validator) } -// Validate a signature using the registered SignatureValidator +// ValidateSignature validates a signature using the registered SignatureValidator func (sv *ForwardingSignatureValidator) ValidateSignature(algorithm string, keyID *string, headerAndPayload string, signature []byte) bool { return (*sv.validator.Load()).ValidateSignature(algorithm, keyID, headerAndPayload, signature) } From b5faaeb8f0b7f0a29fd2fd9cc3299f9f299a53df Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sat, 28 Oct 2023 12:10:28 +0200 Subject: [PATCH 24/25] Use os.ReadFile --- pkg/jwt/configuration.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index b701cda2..0253fdbe 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -157,19 +157,13 @@ func NewSignatureValidatorFromJSONWebKeySetFile(path string, group program.Group } func getJWKSFromFile(path string) (SignatureValidator, error) { - r, err := os.Open(path) - if err != nil { - return nil, err - } - defer r.Close() - - data, err := io.ReadAll(r) + jwksJSON, err := os.ReadFile(path) if err != nil { return nil, err } var jwks jose.JSONWebKeySet - if err := json.Unmarshal(data, &jwks); err != nil { + if err := json.Unmarshal(jwksJSON, &jwks); err != nil { return nil, err } From c42e95f6140b4fd2227603068589ac30f2f59666 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Sat, 28 Oct 2023 12:11:25 +0200 Subject: [PATCH 25/25] Remove unused import --- pkg/jwt/configuration.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/jwt/configuration.go b/pkg/jwt/configuration.go index 0253fdbe..1d3bf937 100644 --- a/pkg/jwt/configuration.go +++ b/pkg/jwt/configuration.go @@ -6,7 +6,6 @@ import ( "crypto/ed25519" "crypto/rsa" "encoding/json" - "io" "log" "os" "reflect"