diff --git a/Dockerfile b/Dockerfile index 931d5859f..9491a1a92 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,7 @@ COPY --chmod=0755 /tools/package-chrome.sh / RUN /package-chrome.sh && rm /package-chrome.sh COPY aws-encryption.yaml / +COPY azure-encryption.yaml / COPY gcp-encryption.yaml / COPY --chmod=0755 run.sh /bin/run.sh diff --git a/azure-encryption.yaml b/azure-encryption.yaml new file mode 100644 index 000000000..537c41b6a --- /dev/null +++ b/azure-encryption.yaml @@ -0,0 +1,10 @@ +apiVersion: apiserver.config.k8s.io/v1 +kind: EncryptionConfiguration +resources: + - resources: + - credentials + providers: + - kms: + apiVersion: v2 + name: azure-kms + endpoint: unix:///tmp/azure-cred-socket.sock diff --git a/pkg/credstores/credstore.go b/pkg/credstores/credstore.go index 4b86960dc..77ffcc46c 100644 --- a/pkg/credstores/credstore.go +++ b/pkg/credstores/credstore.go @@ -18,6 +18,9 @@ import ( type Options struct { AWSKMSKeyARN string GCPKMSKeyURI string + AzureKeyVaultName string + AzureKeyName string + AzureKeyVersion string EncryptionProvider string EncryptionConfigFile string } @@ -34,6 +37,11 @@ func (o *Options) Validate() error { return fmt.Errorf("missing GCP KMS key URI") } o.EncryptionConfigFile = "/gcp-encryption.yaml" + case "azure": + if o.AzureKeyVaultName == "" || o.AzureKeyName == "" || o.AzureKeyVersion == "" { + return fmt.Errorf("missing Azure Key Vault configuration") + } + o.EncryptionConfigFile = "/azure-encryption.yaml" case "custom": if o.EncryptionConfigFile == "" { return fmt.Errorf("missing custom encryption config file") @@ -63,6 +71,10 @@ func Init(ctx context.Context, toolRegistries []string, dsn string, opts Options if err := setUpGoogleKMS(ctx, opts.GCPKMSKeyURI, opts.EncryptionConfigFile); err != nil { return "", nil, fmt.Errorf("failed to setup Google Cloud KMS: %w", err) } + case "azure": + if err := setUpAzureKeyVault(ctx, opts.AzureKeyVaultName, opts.AzureKeyName, opts.AzureKeyVersion, opts.EncryptionConfigFile); err != nil { + return "", nil, fmt.Errorf("failed to setup Azure Key Vault: %w", err) + } } if opts.EncryptionConfigFile != "" { @@ -82,6 +94,45 @@ func Init(ctx context.Context, toolRegistries []string, dsn string, opts Options } } +func setUpAzureKeyVault(ctx context.Context, keyvaultName, keyName, keyVersion, configFile string) error { + if keyvaultName == "" || keyName == "" || keyVersion == "" { + return fmt.Errorf("missing Azure Key Vault configuration") + } + + if err := os.Setenv("GPTSCRIPT_ENCRYPTION_CONFIG_FILE", configFile); err != nil { + return fmt.Errorf("failed to set GPTSCRIPT_ENCRYPTION_CONFIG_FILE: %w", err) + } + + if err := os.WriteFile("/tmp/azure.json", []byte(`{"useManagedIdentityExtension": true}`), 0600); err != nil { + return fmt.Errorf("failed to write Azure config file: %w", err) + } + + cmd := exec.CommandContext(ctx, + "azure-encryption-provider", + "--config-file-path=/tmp/azure.json", + "--listen-addr=unix:///tmp/azure-cred-socket.sock", + "--keyvault-name="+keyvaultName, + "--key-name="+keyName, + "--key-version="+keyVersion, + "--healthz-port=22223") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + return err + } + go func() { + err := cmd.Wait() + select { + case <-ctx.Done(): + // ignore error if we are shutting down + default: + log.Fatalf("azure-encryption-provider exited: %v", err) + } + }() + + return nil +} + func setUpGoogleKMS(ctx context.Context, kmsKeyURI, configFile string) error { if kmsKeyURI == "" { return fmt.Errorf("missing GCP KMS key URI") diff --git a/pkg/services/config.go b/pkg/services/config.go index c5d15ce15..e330cb48c 100644 --- a/pkg/services/config.go +++ b/pkg/services/config.go @@ -69,6 +69,9 @@ type Config struct { HelperModel string `usage:"The model used to generate names and descriptions" default:"gpt-4o-mini"` AWSKMSKeyARN string `usage:"The ARN of the AWS KMS key to use for encrypting credential storage. Only used with the AWS encryption provider." env:"OBOT_AWS_KMS_KEY_ARN" name:"aws-kms-key-arn"` GCPKMSKeyURI string `usage:"The URI of the Google Cloud KMS key to use for encrypting credential storage. Only used with the GCP encryption provider." env:"OBOT_GCP_KMS_KEY_URI" name:"gcp-kms-key-uri"` + AzureKeyVaultName string `usage:"The name of the Azure Key Vault to use for encrypting credential storage. Only used with the Azure encryption provider." env:"OBOT_AZURE_KEY_VAULT_NAME" name:"azure-key-vault-name"` + AzureKeyName string `usage:"The name of the Azure Key Vault key to use for encrypting credential storage. Only used with the Azure encryption provider." env:"OBOT_AZURE_KEY_NAME" name:"azure-key-vault-key-name"` + AzureKeyVersion string `usage:"The version of the Azure Key Vault key to use for encrypting credential storage. Only used with the Azure encryption provider." env:"OBOT_AZURE_KEY_VERSION" name:"azure-key-vault-key-version"` EncryptionProvider string `usage:"The encryption provider to use. Options are AWS, GCP, None, or Custom. Default is None." default:"None"` EncryptionConfigFile string `usage:"The path to the encryption configuration file. Only used with the Custom encryption provider."` EmailServerName string `usage:"The name of the email server to display for email receivers"` @@ -231,6 +234,9 @@ func New(ctx context.Context, config Config) (*Services, error) { credStore, credStoreEnv, err := credstores.Init(ctx, config.ToolRegistries, config.DSN, credstores.Options{ AWSKMSKeyARN: config.AWSKMSKeyARN, GCPKMSKeyURI: config.GCPKMSKeyURI, + AzureKeyVaultName: config.AzureKeyVaultName, + AzureKeyName: config.AzureKeyName, + AzureKeyVersion: config.AzureKeyVersion, EncryptionProvider: config.EncryptionProvider, EncryptionConfigFile: config.EncryptionConfigFile, }) diff --git a/tools/package-tools.sh b/tools/package-tools.sh index fd9403820..f5c516bb6 100755 --- a/tools/package-tools.sh +++ b/tools/package-tools.sh @@ -131,6 +131,18 @@ VERSIONS )" cd .. +if [ ! -e kubernetes-kms ]; then + git clone --depth=1 https://github.com/Azure/kubernetes-kms +fi +cd kubernetes-kms +go build -ldflags="-s -w" -o "${BIN_DIR}/azure-encryption-provider" cmd/server/main.go +OBOT_SERVER_VERSIONS="$(cat <