Skip to content
Draft
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
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ S3_BUCKET = "test-bucket"
S3_REGION = "us-east-1"
YDB_NAME = "local-ydb"
ENABLE_NEW_PATHS_FORMAT = true
ENABLE_BACKUPS_ENCRYPTION = true
# local-ydb image that was built from main
# Image: https://github.com/ydb-platform/ydb/pkgs/container/local-ydb/551703770
# Built from revision 07aeccc41b43c9fc0b7da7680340fbac01b81427
Expand Down
26 changes: 25 additions & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
enable_new_paths_format: [ true, false ]
include:
- enable_new_paths_format: false
enable_backups_encryption: false
- enable_new_paths_format: true
enable_backups_encryption: false
- enable_new_paths_format: true
enable_backups_encryption: true
env:
ENABLE_NEW_PATHS_FORMAT: ${{ matrix.enable_new_paths_format }}
ENABLE_BACKUPS_ENCRYPTION: ${{ matrix.enable_backups_encryption }}
steps:
- uses: actions/checkout@v4
- name: supply with s3 access keys
Expand Down Expand Up @@ -110,3 +117,20 @@ jobs:
if: ${{ matrix.enable_new_paths_format }}
run: |
docker compose down
- name: docker compose up
if: ${{ matrix.enable_backups_encryption }}
run: |
docker compose up -d
- name: run encrypted_backups tests
if: ${{ matrix.enable_backups_encryption }}
run: |
while [ "$(docker inspect -f {{.State.Health.Status}} local-ydbcp)" != "healthy" ]; do
echo "Waiting for container to become healthy..."
sleep 1
done
echo "Starting encrypted_backups tests!"
docker exec local-ydbcp sh -c './test_encrypted_backups'
- name: docker compose down
if: ${{ matrix.enable_backups_encryption }}
run: |
docker compose down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ RUN go build -o ./make_backup ./cmd/integration/make_backup/main.go
RUN go build -o ./list_entities ./cmd/integration/list_entities/main.go
RUN go build -o ./orm ./cmd/integration/orm/main.go
RUN go build -o ./test_new_paths_format ./cmd/integration/new_paths_format/main.go
RUN go build -o ./test_encrypted_backups ./cmd/integration/encrypted_backups/main.go

# Command to run the executable
CMD ["./main", "--config=local_config.yaml"]
Expand Down
27 changes: 25 additions & 2 deletions cmd/ydbcp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"flag"
"fmt"
"github.com/ydb-platform/ydb-go-sdk/v3/log"
"net/http"
_ "net/http/pprof"
"os"
Expand All @@ -19,6 +18,7 @@ import (
"ydbcp/internal/connectors/db/yql/queries"
"ydbcp/internal/connectors/s3"
"ydbcp/internal/handlers"
"ydbcp/internal/kms"
"ydbcp/internal/metrics"
"ydbcp/internal/processor"
"ydbcp/internal/server"
Expand All @@ -32,6 +32,9 @@ import (
"ydbcp/internal/watchers/schedule_watcher"
"ydbcp/internal/watchers/ttl_watcher"
ap "ydbcp/pkg/plugins/auth"
kp "ydbcp/pkg/plugins/kms"

"github.com/ydb-platform/ydb-go-sdk/v3/log"

"github.com/jonboulle/clockwork"

Expand Down Expand Up @@ -114,6 +117,24 @@ func main() {
}
}()
xlog.Info(ctx, "Initialized AuthProvider")

var kmsProvider kp.KmsProvider
if len(configInstance.KMS.PluginPath) == 0 {
kmsProvider, err = kms.NewDummyKmsProvider(ctx)
} else {
kmsProvider, err = kms.NewKmsProvider(ctx, configInstance.KMS)
}
if err != nil {
xlog.Error(ctx, "Error init KmsProvider", zap.Error(err))
os.Exit(1)
}
defer func() {
if err := kmsProvider.Close(ctx); err != nil {
xlog.Error(ctx, "Error close kms provider", zap.Error(err))
}
}()
xlog.Info(ctx, "Initialized KmsProvider")

metrics.InitializeMetricsRegistry(ctx, &wg, &configInstance.MetricsServer, clockwork.NewRealClock())
xlog.Info(ctx, "Initialized metrics registry")
audit.EventsDestination = configInstance.Audit.EventsDestination
Expand Down Expand Up @@ -144,6 +165,7 @@ func main() {
dbConnector,
clientConnector,
authProvider,
kmsProvider,
*configInstance,
).Register(server)
operation.NewOperationService(dbConnector, authProvider).Register(server)
Expand Down Expand Up @@ -192,6 +214,7 @@ func main() {
queries.NewWriteTableQuery,
clockwork.NewRealClock(),
*configInstance,
kmsProvider,
),
); err != nil {
xlog.Error(ctx, "failed to register TBWR handler", zap.Error(err))
Expand All @@ -210,7 +233,7 @@ func main() {
xlog.Info(ctx, "Created TtlWatcher")
}

backupScheduleHandler := handlers.NewBackupScheduleHandler(queries.NewWriteTableQuery, clockwork.NewRealClock())
backupScheduleHandler := handlers.NewBackupScheduleHandler(queries.NewWriteTableQuery, clockwork.NewRealClock(), configInstance.FeatureFlags)

schedule_watcher.NewScheduleWatcher(
ctx, &wg, configInstance.OperationProcessor.ProcessorIntervalSeconds, dbConnector,
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ services:
S3_SECRET_KEY: ${S3_SECRET_KEY}
YDB_NAME: ${YDB_NAME}
ENABLE_NEW_PATHS_FORMAT: ${ENABLE_NEW_PATHS_FORMAT}
ENABLE_BACKUPS_ENCRYPTION: ${ENABLE_BACKUPS_ENCRYPTION}
depends_on:
setup_ydb:
condition: service_completed_successfully
Expand Down
80 changes: 72 additions & 8 deletions internal/backup_operations/make_backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package backup_operations

import (
"context"
"crypto/rand"
"errors"
"fmt"
"github.com/jonboulle/clockwork"
"github.com/ydb-platform/ydb-go-sdk/v3"
"path"
"regexp"
"strings"
Expand All @@ -14,8 +13,12 @@ import (
"ydbcp/internal/connectors/client"
"ydbcp/internal/types"
"ydbcp/internal/util/xlog"
kp "ydbcp/pkg/plugins/kms"
pb "ydbcp/pkg/proto/ydbcp/v1alpha1"

"github.com/jonboulle/clockwork"
"github.com/ydb-platform/ydb-go-sdk/v3"

"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand All @@ -36,6 +39,7 @@ type MakeBackupInternalRequest struct {
ScheduleID *string
Ttl *time.Duration
ParentOperationID *string
EncryptionSettings *pb.EncryptionSettings
}

func FromBackupSchedule(schedule *types.BackupSchedule) MakeBackupInternalRequest {
Expand Down Expand Up @@ -65,6 +69,7 @@ func FromTBWROperation(tbwr *types.TakeBackupWithRetryOperation) MakeBackupInter
ScheduleID: tbwr.ScheduleID,
Ttl: tbwr.Ttl,
ParentOperationID: &tbwr.ID,
EncryptionSettings: tbwr.EncryptionSettings,
}
}

Expand Down Expand Up @@ -282,6 +287,31 @@ func IsEmptyBackup(backup *types.Backup) bool {
return backup.Size == 0 && backup.S3Endpoint == ""
}

func GetEncryptionParams(settings *pb.EncryptionSettings) ([]byte, string, error) {
var algorithm string
var length int

switch settings.Algorithm {
case pb.EncryptionSettings_UNSPECIFIED:
case pb.EncryptionSettings_AES_128_GCM:
algorithm = "AES-128-GCM"
length = 16
case pb.EncryptionSettings_AES_256_GCM:
algorithm = "AES-256-GCM"
length = 32
case pb.EncryptionSettings_CHACHA20_POLY1305:
algorithm = "ChaCha20-Poly1305"
length = 32
}

dek := make([]byte, length)
_, err := rand.Read(dek)
if err != nil {
return nil, "", err
}
return dek, algorithm, nil
}

func MakeBackup(
ctx context.Context,
clientConn client.ClientConnector,
Expand All @@ -292,6 +322,7 @@ func MakeBackup(
subject string,
clock clockwork.Clock,
featureFlags config.FeatureFlagsConfig,
kmsProvider kp.KmsProvider,
) (*types.Backup, *types.TakeBackupOperation, error) {
if req.ScheduleID != nil {
ctx = xlog.With(ctx, zap.String("ScheduleID", *req.ScheduleID))
Expand Down Expand Up @@ -359,6 +390,37 @@ func MakeBackup(
S3ForcePathStyle: s3.S3ForcePathStyle,
}

if req.EncryptionSettings != nil && featureFlags.EnableBackupEncryption {
dek, algorithm, err := GetEncryptionParams(req.EncryptionSettings)
if err != nil {
return nil, nil, err
}

s3Settings.EncryptionKey = dek
s3Settings.EncryptionAlgorithm = algorithm

kmsKey := req.EncryptionSettings.GetKmsKey()
if kmsKey == nil {
xlog.Error(ctx, "kms key is not specified")
return nil, nil, status.Errorf(codes.InvalidArgument, "kms key is not specified")
}

_, err = kmsProvider.Encrypt(
ctx,
&kp.EncryptRequest{
KeyID: kmsKey.GetKeyId(),
Plaintext: dek,
},
)

if err != nil {
xlog.Error(ctx, "can't encrypt data encryption key", zap.Error(err))
return nil, nil, err
}

// TODO: save encrypted key to s3
}

clientOperationID, err := clientConn.ExportToS3(ctx, client, s3Settings, featureFlags)
if err != nil {
xlog.Error(ctx, "can't start export operation", zap.Error(err))
Expand Down Expand Up @@ -388,9 +450,10 @@ func MakeBackup(
CreatedAt: now,
Creator: subject,
},
ScheduleID: req.ScheduleID,
ExpireAt: expireAt,
SourcePaths: pathsForExport,
ScheduleID: req.ScheduleID,
ExpireAt: expireAt,
SourcePaths: pathsForExport,
EncryptionSettings: req.EncryptionSettings,
}

op := &types.TakeBackupOperation{
Expand All @@ -409,9 +472,10 @@ func MakeBackup(
CreatedAt: now,
Creator: subject,
},
YdbOperationId: clientOperationID,
UpdatedAt: now,
ParentOperationID: req.ParentOperationID,
YdbOperationId: clientOperationID,
UpdatedAt: now,
ParentOperationID: req.ParentOperationID,
EncryptionSettings: req.EncryptionSettings,
}

return backup, op, nil
Expand Down
6 changes: 4 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ type MetricsServerConfig struct {
}

type FeatureFlagsConfig struct {
DisableTTLDeletion bool `yaml:"disable_ttl_deletion" default:"false"`
EnableNewPathsFormat bool `yaml:"enable_new_paths_format" default:"false"`
DisableTTLDeletion bool `yaml:"disable_ttl_deletion" default:"false"`
EnableNewPathsFormat bool `yaml:"enable_new_paths_format" default:"false"`
EnableBackupEncryption bool `yaml:"enable_backup_encryption" default:"false"`
}

type LogConfig struct {
Expand Down Expand Up @@ -96,6 +97,7 @@ type Config struct {
ClientConnection ClientConnectionConfig `yaml:"client_connection"`
S3 S3Config `yaml:"s3"`
Auth PluginConfig `yaml:"auth"`
KMS PluginConfig `yaml:"kms"`
GRPCServer GRPCServerConfig `yaml:"grpc_server"`
MetricsServer MetricsServerConfig `yaml:"metrics_server"`
OperationProcessor OperationProcessorConfig `yaml:"operation_processor"`
Expand Down
22 changes: 22 additions & 0 deletions internal/connectors/client/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,17 @@ func (d *ClientYdbConnector) ExportToS3(
exportRequest.Settings.DestinationPrefix = s3Settings.DestinationPrefix
}

if featureFlags.EnableBackupEncryption && len(s3Settings.EncryptionKey) > 0 {
exportRequest.Settings.EncryptionSettings = &Ydb_Export.EncryptionSettings{
EncryptionAlgorithm: s3Settings.EncryptionAlgorithm,
Key: &Ydb_Export.EncryptionSettings_SymmetricKey_{
SymmetricKey: &Ydb_Export.EncryptionSettings_SymmetricKey{
Key: s3Settings.EncryptionKey,
},
},
}
}

response, err := exportClient.ExportToS3(ctx, exportRequest)

if err != nil {
Expand Down Expand Up @@ -425,6 +436,17 @@ func (d *ClientYdbConnector) ImportFromS3(
importRequest.Settings.DestinationPath = path.Join(clientDb.Name(), s3Settings.DestinationPath)
}

if len(s3Settings.EncryptionKey) > 0 {
importRequest.Settings.EncryptionSettings = &Ydb_Export.EncryptionSettings{
EncryptionAlgorithm: s3Settings.EncryptionAlgorithm,
Key: &Ydb_Export.EncryptionSettings_SymmetricKey_{
SymmetricKey: &Ydb_Export.EncryptionSettings_SymmetricKey{
Key: s3Settings.EncryptionKey,
},
},
}
}

response, err := importClient.ImportFromS3(ctx, importRequest)

if err != nil {
Expand Down
Loading
Loading