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

iam auth plugin #34

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/go-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ jobs:
with:
go-version-file: go.mod

- name: Install Protoc
uses: arduino/setup-protoc@v3

- name: Install proto plugins
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.2
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.5.1

- name: Compile protos
run: make proto
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
cmd/ydbcp/ydbcp
plugins/auth_nebius/auth_nebius.so
25 changes: 21 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
FILES := $(filter-out ./pkg/proto/%, $(shell find . -type f -name '*.go'))
PACKAGES ?= $(filter-out ydbcp/pkg/proto/%, $(shell go list ./...))
FILES := $(shell find . -type f -name '*.go' ! -path '*/proto/*' )
PACKAGES := $(foreach path,$(shell go list ./...),$(if $(findstring /proto/,$(path)),,$(path)))
RELEASE_DIR := $(shell pwd)

$(info $$FILES = $(FILES))
$(info $$PACKAGES = $(PACKAGES))
$(info $$RELEASE_DIR = $(RELEASE_DIR))

.PHONY: all

all: test fmt lint proto build

proto:
$(MAKE) -C pkg/proto
$(MAKE) -C plugins/auth_nebius/proto

test:
go test -v ./... -short
Expand All @@ -19,6 +25,17 @@ fmt:
lint:
golint $(PACKAGES)

build: ydbcp
ydbcp:
build: build-plugins build-ydbcp
build-ydbcp:
go build -C cmd/ydbcp -o ydbcp

build-plugins: build-auth_nebius
build-auth_nebius:
go build -C plugins/auth_nebius -buildmode=plugin -o auth_nebius.so

clean:
rm -f plugins/auth_nebius/auth_nebius.so cmd/ydbcp/ydbcp

clean-proto:
find pkg/proto -type f -name '*.go' -delete
find plugins/auth_nebius/proto -type f -name '*.go' -delete
102 changes: 98 additions & 4 deletions cmd/ydbcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import (
_ "go.uber.org/automaxprocs"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/reflection"

"ydbcp/internal/auth"
"ydbcp/internal/config"
configInit "ydbcp/internal/config"
"ydbcp/internal/connectors/client"
Expand All @@ -29,11 +31,14 @@ import (
"ydbcp/internal/processor"
"ydbcp/internal/types"
"ydbcp/internal/util/xlog"
ap "ydbcp/pkg/plugins/auth"
pb "ydbcp/pkg/proto/ydbcp/v1alpha1"
)

var (
port = flag.Int("port", 50051, "The server port")
port = flag.Int("port", 50051, "The server port")
errPermissionDenied = errors.New("permission denied")
errGetAuthToken = errors.New("can't get auth token")
)

// server is used to implement BackupService.
Expand All @@ -43,6 +48,57 @@ type server struct {
driver db.DBConnector
clientConn client.ClientConnector
s3 config.S3Config
auth ap.AuthProvider
}

func tokenFromContext(ctx context.Context) (string, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return "", errGetAuthToken
}
tokens, ok := md["authorization"]
if !ok {
return "", fmt.Errorf("can't find authorization header, %w", errGetAuthToken)
}
if len(tokens) == 0 {
return "", fmt.Errorf("incorrect authorization header format, %w", errGetAuthToken)
}
token := tokens[0]
if len(token) < 8 || token[0:7] != "Bearer " {
return "", fmt.Errorf("incorrect authorization header format, %w", errGetAuthToken)
}
token = token[7:]
return token, nil
}

func (s *server) checkAuth(ctx context.Context, permission, containerID, resourceID string) (string, error) {
token, err := tokenFromContext(ctx)
if err != nil {
xlog.Debug(ctx, "can't get auth token", zap.Error(err))
token = ""
}
check := ap.AuthorizeCheck{
Permission: permission,
ContainerID: containerID,
}
if len(resourceID) > 0 {
check.ResourceID = []string{resourceID}
}

resp, subject, err := s.auth.Authorize(ctx, token, check)
if err != nil {
xlog.Error(ctx, "auth plugin authorize error", zap.Error(err))
return "", errPermissionDenied
}
if len(resp) != 1 {
xlog.Error(ctx, "incorrect auth plugin response length != 1")
return "", errPermissionDenied
}
if resp[0].Code != ap.AuthCodeSuccess {
xlog.Error(ctx, "auth plugin response", zap.Int("code", int(resp[0].Code)), zap.String("message", resp[0].Message))
return "", errPermissionDenied
}
return subject, nil
}

func (s *server) GetBackup(ctx context.Context, request *pb.GetBackupRequest) (*pb.Backup, error) {
Expand All @@ -68,14 +124,24 @@ func (s *server) GetBackup(ctx context.Context, request *pb.GetBackupRequest) (*
return nil, err
}
if len(backups) == 0 {
return nil, errors.New("No backup with such Id")
return nil, errors.New("No backup with such Id") // TODO: Permission denied?
}
// TODO: Need to check access to backup resource by backupID
if _, err := s.checkAuth(ctx, auth.PermissionBackupGet, backups[0].ContainerID, ""); err != nil {
return nil, err
}

xlog.Debug(ctx, "GetBackup", zap.String("backup", backups[0].String()))
return backups[0].Proto(), nil
}

func (s *server) MakeBackup(ctx context.Context, req *pb.MakeBackupRequest) (*pb.Operation, error) {
xlog.Info(ctx, "MakeBackup", zap.String("request", req.String()))
subject, err := s.checkAuth(ctx, auth.PermissionBackupCreate, req.ContainerId, "")
if err != nil {
return nil, err
}
xlog.Debug(ctx, "MakeBackup", zap.String("subject", subject))

clientConnectionParams := types.YdbConnectionParams{
Endpoint: req.GetDatabaseEndpoint(),
Expand Down Expand Up @@ -104,7 +170,7 @@ func (s *server) MakeBackup(ctx context.Context, req *pb.MakeBackupRequest) (*pb
return nil, fmt.Errorf("can't get S3SecretKey: %w", err)
}

dbNamePath := strings.Replace(req.DatabaseName, "/", "_", -1) // TODO: checking user imput
dbNamePath := strings.Replace(req.DatabaseName, "/", "_", -1) // TODO: checking user input
dbNamePath = strings.Trim(dbNamePath, "_")
dstPrefix := path.Join(s.s3.PathPrefix, dbNamePath)

Expand All @@ -130,7 +196,6 @@ func (s *server) MakeBackup(ctx context.Context, req *pb.MakeBackupRequest) (*pb
xlog.Debug(
ctx, "export operation started", zap.String("clientOperationID", clientOperationID), zap.String("dsn", dsn),
)
//TODO: forbid empty container id

backup := types.Backup{
ContainerID: req.GetContainerId(),
Expand Down Expand Up @@ -178,6 +243,12 @@ func (s *server) MakeBackup(ctx context.Context, req *pb.MakeBackupRequest) (*pb
func (s *server) MakeRestore(ctx context.Context, req *pb.MakeRestoreRequest) (*pb.Operation, error) {
xlog.Info(ctx, "MakeRestore", zap.String("request", req.String()))

subject, err := s.checkAuth(ctx, auth.PermissionBackupRestore, req.ContainerId, "") // TODO: check access to backup as resource
if err != nil {
return nil, err
}
xlog.Debug(ctx, "MakeRestore", zap.String("subject", subject))

clientConnectionParams := types.YdbConnectionParams{
Endpoint: req.GetDatabaseEndpoint(),
DatabaseName: req.GetDatabaseName(),
Expand Down Expand Up @@ -248,6 +319,10 @@ func (s *server) MakeRestore(ctx context.Context, req *pb.MakeRestoreRequest) (*

func (s *server) ListBackups(ctx context.Context, request *pb.ListBackupsRequest) (*pb.ListBackupsResponse, error) {
xlog.Debug(ctx, "ListBackups", zap.String("request", request.String()))
if _, err := s.checkAuth(ctx, auth.PermissionBackupList, request.ContainerId, ""); err != nil {
return nil, err
}

queryFilters := make([]queries.QueryFilter, 0)
//TODO: forbid empty containerId
if request.GetContainerId() != "" {
Expand All @@ -271,6 +346,7 @@ func (s *server) ListBackups(ctx context.Context, request *pb.ListBackupsRequest
},
)
}

backups, err := s.driver.SelectBackups(
ctx, queries.NewReadTableQuery(
queries.WithTableName("Backups"),
Expand All @@ -294,6 +370,10 @@ func (s *server) ListOperations(ctx context.Context, request *pb.ListOperationsR
*pb.ListOperationsResponse, error,
) {
xlog.Debug(ctx, "ListOperations", zap.String("request", request.String()))
if _, err := s.checkAuth(ctx, auth.PermissionBackupList, request.ContainerId, ""); err != nil {
return nil, err
}

queryFilters := make([]queries.QueryFilter, 0)
//TODO: forbid empty containerId
if request.GetContainerId() != "" {
Expand All @@ -317,6 +397,7 @@ func (s *server) ListOperations(ctx context.Context, request *pb.ListOperationsR
},
)
}

operations, err := s.driver.SelectOperations(
ctx, queries.NewReadTableQuery(
queries.WithTableName("Operations"),
Expand Down Expand Up @@ -401,11 +482,24 @@ func main() {
os.Exit(1)
}
clientConnector := client.NewClientYdbConnector(configInstance.ClientConnection)
var authProvider ap.AuthProvider
if len(configInstance.Auth.PluginPath) == 0 {
authProvider, err = auth.NewDummyAuthProvider(ctx)
} else {
authProvider, err = auth.NewAuthProvider(ctx, configInstance.Auth)

}
if err != nil {
xlog.Error(ctx, "Error init AuthProvider", zap.Error(err))
os.Exit(1)
}
defer authProvider.Finish(ctx)

server := server{
driver: dbConnector,
clientConn: clientConnector,
s3: configInstance.S3,
auth: authProvider,
}
defer server.driver.Close(ctx)

Expand Down
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ require (
github.com/ydb-platform/ydb-go-sdk/v3 v3.75.2
go.uber.org/automaxprocs v1.5.3
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.64.1
google.golang.org/protobuf v1.34.1
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
gopkg.in/yaml.v3 v3.0.1
)

Expand All @@ -28,5 +28,6 @@ require (
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
Expand All @@ -155,6 +157,10 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
Expand All @@ -170,6 +176,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
46 changes: 46 additions & 0 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package auth

import (
"context"
"fmt"
"plugin"

"ydbcp/internal/config"
"ydbcp/internal/util/xlog"
"ydbcp/pkg/plugins/auth"

"go.uber.org/zap"
)

const (
PermissionBackupList = "ydb.databases.list"
PermissionBackupCreate = "ydb.databases.backup"
PermissionBackupRestore = "ydb.tables.create"
PermissionBackupGet = "ydb.databases.get"
)

func NewAuthProvider(ctx context.Context, cfg config.AuthConfig) (auth.AuthProvider, error) {
xlog.Info(ctx, "Loading auth provider plugin", zap.String("path", cfg.PluginPath))

plug, err := plugin.Open(cfg.PluginPath)
if err != nil {
return nil, fmt.Errorf("can't load auth provider plugin, path %s: %w", cfg.PluginPath, err)
}
symbol, err := plug.Lookup("AuthProvider")
if err != nil {
return nil, fmt.Errorf("can't lookup AuthProvider symbol, plugin path %s: %w", cfg.PluginPath, err)
}
var instance auth.AuthProvider
instance, ok := symbol.(auth.AuthProvider)
if !ok {
return nil, fmt.Errorf("can't cast AuthProvider symbol, plugin path %s", cfg.PluginPath)
}
pluginConfig, err := cfg.ConfigurationString()
if err != nil {
return nil, fmt.Errorf("can't get auth provider configuration: %w", err)
}
if err = instance.Init(ctx, pluginConfig); err != nil {
return nil, fmt.Errorf("can't initialize auth provider plugin: %w", err)
}
return instance, nil
}
Loading
Loading