Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote authenticator and authorizer #234

Merged
merged 7 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/bb_replicator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ func main() {
replicator_pb.RegisterReplicatorServer(s, replication.NewReplicatorServer(replicator))
},
siblingsGroup,
grpcClientFactory,
); err != nil {
return util.StatusWrap(err, "gRPC server failure")
}

lifecycleState.MarkReadyAndWait(siblingsGroup)
lifecycleState.MarkReadyAndWait(siblingsGroup, grpcClientFactory)
return nil
})
}
1 change: 1 addition & 0 deletions cmd/bb_storage/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ go_library(
visibility = ["//visibility:private"],
deps = [
"//pkg/auth",
"//pkg/auth/configuration",
"//pkg/blobstore",
"//pkg/blobstore/configuration",
"//pkg/blobstore/grpcservers",
Expand Down
35 changes: 21 additions & 14 deletions cmd/bb_storage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

remoteexecution "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
"github.com/buildbarn/bb-storage/pkg/auth"
auth_configuration "github.com/buildbarn/bb-storage/pkg/auth/configuration"
"github.com/buildbarn/bb-storage/pkg/blobstore"
blobstore_configuration "github.com/buildbarn/bb-storage/pkg/blobstore/configuration"
"github.com/buildbarn/bb-storage/pkg/blobstore/grpcservers"
Expand Down Expand Up @@ -56,7 +57,8 @@ func main() {
configuration.ContentAddressableStorage,
blobstore_configuration.NewCASBlobAccessCreator(
grpcClientFactory,
int(configuration.MaximumMessageSizeBytes)))
int(configuration.MaximumMessageSizeBytes)),
grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create Content Addressable Storage")
}
Expand All @@ -75,7 +77,8 @@ func main() {
blobstore_configuration.NewACBlobAccessCreator(
contentAddressableStorageInfo,
grpcClientFactory,
int(configuration.MaximumMessageSizeBytes)))
int(configuration.MaximumMessageSizeBytes)),
grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create Action Cache")
}
Expand All @@ -94,7 +97,8 @@ func main() {
configuration.IndirectContentAddressableStorage,
blobstore_configuration.NewICASBlobAccessCreator(
grpcClientFactory,
int(configuration.MaximumMessageSizeBytes)))
int(configuration.MaximumMessageSizeBytes)),
grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create Indirect Content Addressable Storage")
}
Expand All @@ -109,7 +113,8 @@ func main() {
configuration.InitialSizeClassCache,
blobstore_configuration.NewISCCBlobAccessCreator(
grpcClientFactory,
int(configuration.MaximumMessageSizeBytes)))
int(configuration.MaximumMessageSizeBytes)),
grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create Initial Size Class Cache")
}
Expand All @@ -124,7 +129,8 @@ func main() {
configuration.FileSystemAccessCache,
blobstore_configuration.NewFSACBlobAccessCreator(
grpcClientFactory,
int(configuration.MaximumMessageSizeBytes)))
int(configuration.MaximumMessageSizeBytes)),
grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create File System Access Cache")
}
Expand All @@ -148,7 +154,7 @@ func main() {
if err != nil {
return err
}
executeAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetExecuteAuthorizer())
executeAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetExecuteAuthorizer(), grpcClientFactory)
if err != nil {
return util.StatusWrap(err, "Failed to create execute authorizer")
}
Expand Down Expand Up @@ -210,26 +216,27 @@ func main() {
}
},
siblingsGroup,
grpcClientFactory,
); err != nil {
return util.StatusWrap(err, "gRPC server failure")
}

lifecycleState.MarkReadyAndWait(siblingsGroup)
lifecycleState.MarkReadyAndWait(siblingsGroup, grpcClientFactory)
return nil
})
}

func newNonScannableBlobAccess(dependenciesGroup program.Group, configuration *bb_storage.NonScannableBlobAccessConfiguration, creator blobstore_configuration.BlobAccessCreator) (blobstore_configuration.BlobAccessInfo, blobstore.BlobAccess, []auth.Authorizer, auth.Authorizer, error) {
func newNonScannableBlobAccess(dependenciesGroup program.Group, configuration *bb_storage.NonScannableBlobAccessConfiguration, creator blobstore_configuration.BlobAccessCreator, grpcClientFactory bb_grpc.ClientFactory) (blobstore_configuration.BlobAccessInfo, blobstore.BlobAccess, []auth.Authorizer, auth.Authorizer, error) {
info, err := blobstore_configuration.NewBlobAccessFromConfiguration(dependenciesGroup, configuration.Backend, creator)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, nil, err
}

getAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetAuthorizer)
getAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetAuthorizer, grpcClientFactory)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, nil, util.StatusWrap(err, "Failed to create Get() authorizer")
}
putAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.PutAuthorizer)
putAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.PutAuthorizer, grpcClientFactory)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, nil, util.StatusWrap(err, "Failed to create Put() authorizer")
}
Expand All @@ -241,21 +248,21 @@ func newNonScannableBlobAccess(dependenciesGroup program.Group, configuration *b
nil
}

func newScannableBlobAccess(dependenciesGroup program.Group, configuration *bb_storage.ScannableBlobAccessConfiguration, creator blobstore_configuration.BlobAccessCreator) (blobstore_configuration.BlobAccessInfo, blobstore.BlobAccess, []auth.Authorizer, error) {
func newScannableBlobAccess(dependenciesGroup program.Group, configuration *bb_storage.ScannableBlobAccessConfiguration, creator blobstore_configuration.BlobAccessCreator, grpcClientFactory bb_grpc.ClientFactory) (blobstore_configuration.BlobAccessInfo, blobstore.BlobAccess, []auth.Authorizer, error) {
info, err := blobstore_configuration.NewBlobAccessFromConfiguration(dependenciesGroup, configuration.Backend, creator)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, err
}

getAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetAuthorizer)
getAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.GetAuthorizer, grpcClientFactory)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, util.StatusWrap(err, "Failed to create Get() authorizer")
}
putAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.PutAuthorizer)
putAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.PutAuthorizer, grpcClientFactory)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, util.StatusWrap(err, "Failed to create Put() authorizer")
}
findMissingAuthorizer, err := auth.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.FindMissingAuthorizer)
findMissingAuthorizer, err := auth_configuration.DefaultAuthorizerFactory.NewAuthorizerFromConfiguration(configuration.FindMissingAuthorizer, grpcClientFactory)
if err != nil {
return blobstore_configuration.BlobAccessInfo{}, nil, nil, util.StatusWrap(err, "Failed to create FindMissing() authorizer")
}
Expand Down
1 change: 1 addition & 0 deletions internal/mock/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ gomock(
out = "auth.go",
interfaces = [
"Authorizer",
"RequestHeadersAuthenticator",
],
library = "//pkg/auth",
mockgen_model_library = "@org_uber_go_mock//mockgen/model",
Expand Down
16 changes: 14 additions & 2 deletions pkg/auth/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,29 @@ go_library(
"any_authorizer.go",
"authentication_metadata.go",
"authorizer.go",
"authorizer_factory.go",
"jmespath_expression_authorizer.go",
"remote_authorizer.go",
"remote_request_headers_authenticator.go",
"request_headers_authenticator.go",
"static_authorizer.go",
],
importpath = "github.com/buildbarn/bb-storage/pkg/auth",
visibility = ["//visibility:public"],
deps = [
"//pkg/clock",
"//pkg/digest",
"//pkg/eviction",
"//pkg/otel",
"//pkg/proto/auth",
"//pkg/proto/configuration/auth",
"//pkg/util",
"@com_github_jmespath_go_jmespath//:go-jmespath",
"@io_opentelemetry_go_otel//attribute",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/structpb",
],
)

Expand All @@ -33,21 +38,28 @@ go_test(
"any_authorizer_test.go",
"authentication_metadata_test.go",
"jmespath_expression_authorizer_test.go",
"remote_authorizer_test.go",
"remote_request_headers_authenticator_test.go",
"static_authorizer_test.go",
],
deps = [
":auth",
"//internal/mock",
"//pkg/digest",
"//pkg/eviction",
"//pkg/proto/auth",
"//pkg/testutil",
"@com_github_jmespath_go_jmespath//:go-jmespath",
"@com_github_stretchr_testify//require",
"@io_opentelemetry_go_otel//attribute",
"@io_opentelemetry_go_proto_otlp//common/v1:common",
"@org_golang_google_grpc//:grpc",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/emptypb",
"@org_golang_google_protobuf//types/known/structpb",
"@org_golang_google_protobuf//types/known/timestamppb",
"@org_uber_go_mock//gomock",
],
)
21 changes: 21 additions & 0 deletions pkg/auth/configuration/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "configuration",
srcs = ["authorizer_factory.go"],
importpath = "github.com/buildbarn/bb-storage/pkg/auth/configuration",
visibility = ["//visibility:public"],
deps = [
"//pkg/auth",
"//pkg/clock",
"//pkg/digest",
"//pkg/eviction",
"//pkg/grpc",
"//pkg/proto/configuration/auth",
"//pkg/util",
"@com_github_jmespath_go_jmespath//:go-jmespath",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//encoding/protojson",
],
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package auth
package configuration

import (
"github.com/buildbarn/bb-storage/pkg/auth"
"github.com/buildbarn/bb-storage/pkg/clock"
"github.com/buildbarn/bb-storage/pkg/digest"
"github.com/buildbarn/bb-storage/pkg/eviction"
"github.com/buildbarn/bb-storage/pkg/grpc"
pb "github.com/buildbarn/bb-storage/pkg/proto/configuration/auth"
"github.com/buildbarn/bb-storage/pkg/util"
"github.com/jmespath/go-jmespath"
Expand All @@ -16,7 +20,7 @@ import (
type AuthorizerFactory interface {
// NewAuthorizerFromConfiguration constructs an authorizer based on
// options specified in a configuration message.
NewAuthorizerFromConfiguration(configuration *pb.AuthorizerConfiguration) (Authorizer, error)
NewAuthorizerFromConfiguration(configuration *pb.AuthorizerConfiguration, grpcClientFactory grpc.ClientFactory) (auth.Authorizer, error)
}

// DefaultAuthorizerFactory constructs deduplicated authorizers based on
Expand All @@ -29,15 +33,15 @@ type BaseAuthorizerFactory struct{}

// NewAuthorizerFromConfiguration constructs an authorizer based on
// options specified in a configuration message.
func (f BaseAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.AuthorizerConfiguration) (Authorizer, error) {
func (f BaseAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.AuthorizerConfiguration, grpcClientFactory grpc.ClientFactory) (auth.Authorizer, error) {
if config == nil {
return nil, status.Error(codes.InvalidArgument, "Authorizer configuration not specified")
}
switch policy := config.Policy.(type) {
case *pb.AuthorizerConfiguration_Allow:
return NewStaticAuthorizer(func(in digest.InstanceName) bool { return true }), nil
return auth.NewStaticAuthorizer(func(in digest.InstanceName) bool { return true }), nil
case *pb.AuthorizerConfiguration_Deny:
return NewStaticAuthorizer(func(in digest.InstanceName) bool { return false }), nil
return auth.NewStaticAuthorizer(func(in digest.InstanceName) bool { return false }), nil
case *pb.AuthorizerConfiguration_InstanceNamePrefix:
trie := digest.NewInstanceNameTrie()
for _, i := range policy.InstanceNamePrefix.AllowedInstanceNamePrefixes {
Expand All @@ -47,13 +51,29 @@ func (f BaseAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.Authori
}
trie.Set(instanceNamePrefix, 0)
}
return NewStaticAuthorizer(trie.ContainsPrefix), nil
return auth.NewStaticAuthorizer(trie.ContainsPrefix), nil
case *pb.AuthorizerConfiguration_JmespathExpression:
expression, err := jmespath.Compile(policy.JmespathExpression)
if err != nil {
return nil, util.StatusWrapWithCode(err, codes.InvalidArgument, "Failed to compile JMESPath expression")
}
return NewJMESPathExpressionAuthorizer(expression), nil
return auth.NewJMESPathExpressionAuthorizer(expression), nil
case *pb.AuthorizerConfiguration_Remote:
grpcClient, err := grpcClientFactory.NewClientFromConfiguration(policy.Remote.Endpoint)
if err != nil {
return nil, util.StatusWrap(err, "Failed to create authorizer RPC client")
}
evictionSet, err := eviction.NewSetFromConfiguration[auth.RemoteAuthorizerCacheKey](policy.Remote.CacheReplacementPolicy)
if err != nil {
return nil, util.StatusWrap(err, "Cache replacement policy for remote authorization")
}
return auth.NewRemoteAuthorizer(
grpcClient,
policy.Remote.Scope,
clock.SystemClock,
eviction.NewMetricsSet(evictionSet, "remote_authorizer"),
int(policy.Remote.MaximumCacheSize),
), nil
default:
return nil, status.Error(codes.InvalidArgument, "Unknown authorizer configuration")
}
Expand All @@ -62,7 +82,7 @@ func (f BaseAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.Authori
type deduplicatingAuthorizerFactory struct {
base AuthorizerFactory
// Keys are protojson-encoded pb.AuthorizerConfigurations
known map[string]Authorizer
known map[string]auth.Authorizer
}

// NewDeduplicatingAuthorizerFactory creates a new AuthorizerFactory
Expand All @@ -71,19 +91,19 @@ type deduplicatingAuthorizerFactory struct {
func NewDeduplicatingAuthorizerFactory(base AuthorizerFactory) AuthorizerFactory {
return &deduplicatingAuthorizerFactory{
base: base,
known: make(map[string]Authorizer),
known: make(map[string]auth.Authorizer),
}
}

// NewAuthorizerFromConfiguration creates an Authorizer based on the passed configuration.
func (af *deduplicatingAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.AuthorizerConfiguration) (Authorizer, error) {
func (af *deduplicatingAuthorizerFactory) NewAuthorizerFromConfiguration(config *pb.AuthorizerConfiguration, grpcClientFactory grpc.ClientFactory) (auth.Authorizer, error) {
keyBytes, err := protojson.Marshal(config)
key := string(keyBytes)
if err != nil {
return nil, err
}
if _, ok := af.known[key]; !ok {
a, err := af.base.NewAuthorizerFromConfiguration(config)
a, err := af.base.NewAuthorizerFromConfiguration(config, grpcClientFactory)
if err != nil {
return nil, err
}
Expand Down
Loading