Skip to content

Commit

Permalink
Merge pull request #80 from BitPatty/master
Browse files Browse the repository at this point in the history
Add SSL Certificates options
  • Loading branch information
JamesClonk committed Oct 2, 2023
2 parents e74ca03 + 0c15899 commit 52d1759
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 2 deletions.
30 changes: 30 additions & 0 deletions config/config.go
Expand Up @@ -16,6 +16,15 @@ var (
configFile string = "config.json"
)

type SSLConfig struct {
CACertPath string `json:"ca_cert_path"`
ClientCertPath string `json:"client_cert_path"`
ClientKeyPath string `json:"client_key_path"`
PEMKeyPassword string `json:"pem_key_password"`
PEMKeyPath string `json:"pem_key_path"`
VerifyServerCert bool `json:"verify_server_cert"`
}

type Config struct {
Port int
LogLevel string `json:"log_level"`
Expand All @@ -34,6 +43,7 @@ type Config struct {
Services map[string]Service
ServiceBindingRoot string `json:"service_binding_root"`
Foreground bool
SSL SSLConfig `json:"ssl"`
}

type S3Config struct {
Expand Down Expand Up @@ -247,6 +257,26 @@ func new() *Config {
mergedServiceConfig.Binding.Database = serviceConfig.Binding.Database
}

// ssl/tls
if len(serviceConfig.Binding.SSL.CACertPath) > 0 {
mergedServiceConfig.Binding.SSL.CACertPath = serviceConfig.Binding.SSL.CACertPath
}
if len(serviceConfig.Binding.SSL.ClientCertPath) > 0 {
mergedServiceConfig.Binding.SSL.ClientCertPath = serviceConfig.Binding.SSL.ClientCertPath
}
if len(serviceConfig.Binding.SSL.ClientKeyPath) > 0 {
mergedServiceConfig.Binding.SSL.ClientKeyPath = serviceConfig.Binding.SSL.ClientKeyPath
}
if len(serviceConfig.Binding.SSL.PEMKeyPassword) > 0 {
mergedServiceConfig.Binding.SSL.PEMKeyPassword = serviceConfig.Binding.SSL.PEMKeyPassword
}
if len(serviceConfig.Binding.SSL.PEMKeyPath) > 0 {
mergedServiceConfig.Binding.SSL.PEMKeyPath = serviceConfig.Binding.SSL.PEMKeyPath
}
if serviceConfig.Binding.SSL.VerifyServerCert {
mergedServiceConfig.Binding.SSL.VerifyServerCert = serviceConfig.Binding.SSL.VerifyServerCert
}

config.Services[serviceName] = mergedServiceConfig
}
}
Expand Down
11 changes: 11 additions & 0 deletions config/service.go
Expand Up @@ -24,6 +24,16 @@ type Service struct {
// order of precedence: SERVICE_BINDING_ROOT > VCAP_SERVICES > Config.Services.Binding
Binding ServiceBinding `json:"service_binding"` // optional
}

type SSLConfiguration struct {
ClientCertPath string
CACertPath string
ClientKeyPath string
PEMKeyPassword string
PEMKeyPath string
VerifyServerCert bool
}

type ServiceBinding struct {
Type string
Provider string
Expand All @@ -34,6 +44,7 @@ type ServiceBinding struct {
Username string
Password string
Database string
SSL SSLConfiguration
}
type ServiceRetention struct {
Days int
Expand Down
20 changes: 19 additions & 1 deletion docs/configuration.md
Expand Up @@ -87,6 +87,12 @@ A more comprehensive example of possible configuration options could look like t
"username": "my-db-user",
"Password": "db-pass",
"database": "dbname-to-backup",
"ssl": {
"ca_cert_path": "/path/to/ca-cert.pem",
"client_cert_path": "/path/to/client-cert.pem",
"client_key_path": "/path/to/client-key.pem",
"verify_server_cert": true
}
}
}
...
Expand Down Expand Up @@ -127,6 +133,12 @@ or through the specific environment variables `$BACKMAN_USERNAME` and `$BACKMAN_
- `s3.service_type`: optional, defines which service type or label backman will look for in service instances to find the S3-compatible object storage
- `s3.bucket_name`: optional, bucket to use on S3 storage, backman will use service-instance/binding-name if not configured
- `s3.encryption_key`: optional, defines the key which will be used to encrypt and decrypt backups as they are stored on the S3 can also be passed as an environment variable with the name `$BACKMAN_ENCRYPTION_KEY`
- `ssl.ca_cert_path`: optional, the path to the CA certificate (MySQL, PostgreSQL and MongoDB only)
- `ssl.client_cert_path`: optional, the path to the client certificate (MySQL and PostgreSQL only)
- `ssl.client_key_path`: optional, the path to the client key (MySQL and PostgreSQL only)
- `ssl.pem_key_password`: optional, the password for the PEM Key (MongoDB only)
- `ssl.pem_key_path`: optional, the path to the PEM Key (MongoDB only)
- `ssl.verify_server_cert`: optional, whether to verify the server certificate (MySQL and PostgreSQL only)

> **Note**: Usage of `s3.encryption_key` is not backward compatible! Backups generated without or with a different encryption key than before cannot be downloaded or restored anymore.
Expand Down Expand Up @@ -180,7 +192,13 @@ Here's an example with S3 (for backup storage) and 2 services with explicitely c
"username": "admin",
"password": "d44b5e36-7c3f-433a-b244-3e8d2a8e2e22",
"port": 5432,
"database": "productdb"
"database": "productdb",
"ssl": {
"ca_cert_path": "/path/to/ca-cert.pem",
"client_cert_path": "/path/to/client-cert.pem",
"client_key_path": "/path/to/client-key.pem",
"verify_server_cert": true
}
}
},
"cookie-cache": {
Expand Down
17 changes: 17 additions & 0 deletions service/mongodb/backup.go
Expand Up @@ -39,6 +39,23 @@ func Backup(ctx context.Context, s3 *s3.Client, service config.Service, filename
command = append(command, "--archive")
command = append(command, service.BackupOptions...)

// ssl/tls
if len(service.Binding.SSL.PEMKeyPath) > 0 || len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--ssl")
}

if len(service.Binding.SSL.PEMKeyPath) > 0 {
command = append(command, "--sslPEMKeyFile="+service.Binding.SSL.PEMKeyPath)

if len(service.Binding.SSL.PEMKeyPassword) > 0 {
command = append(command, "--sslPEMKeyPassword='"+service.Binding.SSL.PEMKeyPassword+"'")
}
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--sslCAFile="+service.Binding.SSL.CACertPath)
}

log.Debugf("executing mongodb backup command: %v", strings.Join(command, " "))
cmd := exec.CommandContext(ctx, command[0], command[1:]...)

Expand Down
17 changes: 17 additions & 0 deletions service/mongodb/restore.go
Expand Up @@ -34,6 +34,23 @@ func Restore(ctx context.Context, s3 *s3.Client, service config.Service, target
command = append(command, "--archive")
command = append(command, service.RestoreOptions...)

// ssl/tls
if len(service.Binding.SSL.PEMKeyPath) > 0 || len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--ssl")
}

if len(service.Binding.SSL.PEMKeyPath) > 0 {
command = append(command, "--sslPEMKeyFile="+service.Binding.SSL.PEMKeyPath)

if len(service.Binding.SSL.PEMKeyPassword) > 0 {
command = append(command, "--sslPEMKeyPassword='"+service.Binding.SSL.PEMKeyPassword+"'")
}
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--sslCAFile="+service.Binding.SSL.CACertPath)
}

log.Debugf("executing mongodb restore command: %v", strings.Join(command, " "))
cmd := exec.CommandContext(ctx, command[0], command[1:]...)

Expand Down
18 changes: 18 additions & 0 deletions service/mysql/backup.go
Expand Up @@ -50,6 +50,24 @@ func Backup(ctx context.Context, s3 *s3.Client, service config.Service, filename
command = append(command, strconv.Itoa(service.Binding.Port))
command = append(command, "-u")
command = append(command, service.Binding.Username)

// ssl/tls
if len(service.Binding.SSL.ClientCertPath) > 0 {
command = append(command, "--ssl-cert="+service.Binding.SSL.ClientCertPath)
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--ssl-ca="+service.Binding.SSL.CACertPath)
}

if len(service.Binding.SSL.ClientKeyPath) > 0 {
command = append(command, "--ssl-key="+service.Binding.SSL.ClientKeyPath)
}

if service.Binding.SSL.VerifyServerCert {
command = append(command, "--ssl-verify-server-cert")
}

if len(service.Binding.Database) > 0 {
command = append(command, "--no-create-db")
command = append(command, service.Binding.Database)
Expand Down
18 changes: 18 additions & 0 deletions service/mysql/restore.go
Expand Up @@ -41,6 +41,24 @@ func Restore(ctx context.Context, s3 *s3.Client, service config.Service, target
command = append(command, strconv.Itoa(target.Binding.Port))
command = append(command, "-u")
command = append(command, target.Binding.Username)

// ssl/tls
if len(service.Binding.SSL.ClientCertPath) > 0 {
command = append(command, "--ssl-cert="+service.Binding.SSL.ClientCertPath)
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "--ssl-ca="+service.Binding.SSL.CACertPath)
}

if len(service.Binding.SSL.ClientKeyPath) > 0 {
command = append(command, "--ssl-key="+service.Binding.SSL.ClientKeyPath)
}

if service.Binding.SSL.VerifyServerCert {
command = append(command, "--ssl-verify-server-cert")
}

// https://stackoverflow.com/questions/11263018/mysql-ignore-errors-when-importing/25771417#25771417
if service.ForceImport {
command = append(command, "--force")
Expand Down
19 changes: 18 additions & 1 deletion service/postgres/backup.go
Expand Up @@ -49,6 +49,24 @@ func Backup(ctx context.Context, s3 *s3.Client, service config.Service, filename
}
command = append(command, "-c")
command = append(command, "--no-password")

// ssl/tls
if len(service.Binding.SSL.ClientCertPath) > 0 {
command = append(command, "sslcert="+service.Binding.SSL.ClientCertPath)
}

if len(service.Binding.SSL.ClientKeyPath) > 0 {
command = append(command, "sslkey="+service.Binding.SSL.ClientKeyPath)
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "sslrootcert="+service.Binding.SSL.CACertPath)
}

if service.Binding.SSL.VerifyServerCert {
command = append(command, "sslmode=verify-ca")
}

command = append(command, service.BackupOptions...)

// store backup file locally first, before uploading it onto s3
Expand Down Expand Up @@ -203,5 +221,4 @@ func Backup(ctx context.Context, s3 *s3.Client, service config.Service, filename
}
return err
}
return nil
}
18 changes: 18 additions & 0 deletions service/postgres/restore.go
Expand Up @@ -37,6 +37,24 @@ func Restore(ctx context.Context, s3 *s3.Client, service config.Service, target
var command []string
command = append(command, "psql")
command = append(command, "--quiet")

// ssl/tls
if len(service.Binding.SSL.ClientCertPath) > 0 {
command = append(command, "sslcert="+service.Binding.SSL.ClientCertPath)
}

if len(service.Binding.SSL.ClientKeyPath) > 0 {
command = append(command, "sslkey="+service.Binding.SSL.ClientKeyPath)
}

if len(service.Binding.SSL.CACertPath) > 0 {
command = append(command, "sslrootcert="+service.Binding.SSL.CACertPath)
}

if service.Binding.SSL.VerifyServerCert {
command = append(command, "sslmode=verify-ca")
}

command = append(command, service.RestoreOptions...)
command = append(command, target.Binding.Database)

Expand Down

0 comments on commit 52d1759

Please sign in to comment.