Skip to content

Commit 597b1e1

Browse files
authored
Merge pull request #300 from databacker/restructure-config-file
restructure config file
2 parents 0fad8e2 + 4504145 commit 597b1e1

File tree

18 files changed

+1079
-418
lines changed

18 files changed

+1079
-418
lines changed

TODO.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

cmd/root.go

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/spf13/cobra"
1616
"github.com/spf13/pflag"
1717
"github.com/spf13/viper"
18-
"gopkg.in/yaml.v3"
1918
)
2019

2120
type execs interface {
@@ -32,7 +31,7 @@ var subCommands = []subCommand{dumpCmd, restoreCmd, pruneCmd}
3231
type cmdConfiguration struct {
3332
dbconn database.Connection
3433
creds credentials.Creds
35-
configuration *config.Config
34+
configuration *config.ConfigSpec
3635
}
3736

3837
const (
@@ -71,41 +70,43 @@ func rootCmd(execs execs) (*cobra.Command, error) {
7170
// read the config file, if needed; the structure of the config differs quite some
7271
// from the necessarily flat env vars/CLI flags, so we can't just use viper's
7372
// automatic config file support.
74-
if configFile := v.GetString("config-file"); configFile != "" {
73+
var actualConfig *config.ConfigSpec
74+
75+
if configFilePath := v.GetString("config-file"); configFilePath != "" {
7576
var (
76-
f *os.File
77-
err error
78-
config config.Config
77+
f *os.File
78+
err error
7979
)
80-
if f, err = os.Open(configFile); err != nil {
80+
if f, err = os.Open(configFilePath); err != nil {
8181
return fmt.Errorf("fatal error config file: %w", err)
8282
}
8383
defer f.Close()
84-
decoder := yaml.NewDecoder(f)
85-
if err := decoder.Decode(&config); err != nil {
86-
return fmt.Errorf("fatal error config file: %w", err)
84+
actualConfig, err = config.ProcessConfig(f)
85+
if err != nil {
86+
return fmt.Errorf("unable to read provided config: %w", err)
8787
}
88-
cmdConfig.configuration = &config
8988
}
9089

9190
// the structure of our config file is more complex and with relationships than our config/env var
9291
// so we cannot use a single viper structure, as described above.
9392

9493
// set up database connection
95-
if cmdConfig.configuration != nil {
96-
if cmdConfig.configuration.Database.Server != "" {
97-
cmdConfig.dbconn.Host = cmdConfig.configuration.Database.Server
94+
if actualConfig != nil {
95+
if actualConfig.Database.Server != "" {
96+
cmdConfig.dbconn.Host = actualConfig.Database.Server
9897
}
99-
if cmdConfig.configuration.Database.Port != 0 {
100-
cmdConfig.dbconn.Port = cmdConfig.configuration.Database.Port
98+
if actualConfig.Database.Port != 0 {
99+
cmdConfig.dbconn.Port = actualConfig.Database.Port
101100
}
102-
if cmdConfig.configuration.Database.Credentials.Username != "" {
103-
cmdConfig.dbconn.User = cmdConfig.configuration.Database.Credentials.Username
101+
if actualConfig.Database.Credentials.Username != "" {
102+
cmdConfig.dbconn.User = actualConfig.Database.Credentials.Username
104103
}
105-
if cmdConfig.configuration.Database.Credentials.Password != "" {
106-
cmdConfig.dbconn.Pass = cmdConfig.configuration.Database.Credentials.Password
104+
if actualConfig.Database.Credentials.Password != "" {
105+
cmdConfig.dbconn.Pass = actualConfig.Database.Credentials.Password
107106
}
107+
cmdConfig.configuration = actualConfig
108108
}
109+
109110
// override config with env var or CLI flag, if set
110111
dbHost := v.GetString("server")
111112
if dbHost != "" && v.IsSet("server") {

cmd/testdata/config.yml

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
database:
2-
server: abcd
3-
port: 3306
4-
credentials:
5-
username: user2
6-
password: xxxx2
1+
version: config.databack.io/v1
2+
kind: local
73

8-
targets:
9-
local:
10-
type: file
11-
url: file:///foo/bar
12-
other:
13-
type: file
14-
url: /foo/bar
4+
spec:
5+
database:
6+
server: abcd
7+
port: 3306
8+
credentials:
9+
username: user2
10+
password: xxxx2
1511

16-
dump:
1712
targets:
18-
- local
13+
local:
14+
type: file
15+
url: file:///foo/bar
16+
other:
17+
type: file
18+
url: /foo/bar
1919

20-
prune:
21-
retention: "1h"
20+
dump:
21+
targets:
22+
- local
23+
24+
prune:
25+
retention: "1h"

docs/configuration.md

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,15 @@ The following are the environment variables, CLI flags and configuration file op
6666
| port to use to connect to database. Optional. | BR | `port` | `DB_PORT` | `database.port` | 3306 |
6767
| username for the database | BR | `user` | `DB_USER` | `database.credentials.username` | |
6868
| password for the database | BR | `pass` | `DB_PASS` | `database.credentials.password` | |
69-
| names of databases to dump, comma-separated | B | `include` | `DB_NAMES` | `database.include` | all databases in the server |
70-
| names of databases to exclude from the dump | B | `exclude` | `DB_NAMES_EXCLUDE` | `database.exclude` | |
71-
| do not include `USE <database>;` statement in the dump | B | `no-database-name` | `NO_DATABASE_NAME` | `database.no-database-name` | `false` |
69+
| names of databases to dump, comma-separated | B | `include` | `DB_NAMES` | `dump.include` | all databases in the server |
70+
| names of databases to exclude from the dump | B | `exclude` | `DB_NAMES_EXCLUDE` | `dump.exclude` | |
71+
| do not include `USE <database>;` statement in the dump | B | `no-database-name` | `NO_DATABASE_NAME` | `dump.no-database-name` | `false` |
7272
| restore to a specific database | R | `restore --database` | `RESTORE_DATABASE` | `restore.database` | |
7373
| how often to do a dump or prune, in minutes | BP | `dump --frequency` | `DB_DUMP_FREQ` | `dump.schedule.frequency` | `1440` (in minutes), i.e. once per day |
7474
| what time to do the first dump or prune | BP | `dump --begin` | `DB_DUMP_BEGIN` | `dump.schedule.begin` | `0`, i.e. immediately |
7575
| cron schedule for dumps or prunes | BP | `dump --cron` | `DB_DUMP_CRON` | `dump.schedule.cron` | |
7676
| run the backup or prune a single time and exit | BP | `dump --once` | `RUN_ONCE` | `dump.schedule.once` | `false` |
77-
| enable debug logging | BRP | `debug` | `DEBUG` | `logging: debug` | `false` |
77+
| enable debug logging | BRP | `debug` | `DEBUG` | `logging` | `false` |
7878
| where to put the dump file; see [backup](./backup.md) | BP | `dump --target` | `DB_DUMP_TARGET` | `dump.targets` | |
7979
| where the restore file exists; see [restore](./restore.md) | R | `restore --target` | `DB_RESTORE_TARGET` | `restore.target` | |
8080
| replace any `:` in the dump filename with `-` | BP | `dump --safechars` | `DB_DUMP_SAFECHARS` | `database.safechars` | `false` |
@@ -89,6 +89,103 @@ The following are the environment variables, CLI flags and configuration file op
8989
| filename to save the target backup file | B | `dump --filename-pattern` | `DB_DUMP_FILENAME_PATTERN` | `dump.filename-pattern` | |
9090
| directory with scripts to execute before backup | B | `dump --pre-backup-scripts` | `DB_DUMP_PRE_BACKUP_SCRIPTS` | `dump.scripts.pre-backup` | in container, `/scripts.d/pre-backup/` |
9191
| directory with scripts to execute after backup | B | `dump --post-backup-scripts` | `DB_DUMP_POST_BACKUP_SCRIPTS` | `dump.scripts.post-backup` | in container, `/scripts.d/post-backup/` |
92-
| directory with scripts to execute before restore | R | `restore --pre-restore-scripts` | `DB_DUMP_PRE_RESTORE_SCRIPTS` | `dump.pre-restore-scripts` | in container, `/scripts.d/pre-restore/` |
93-
| directory with scripts to execute after restore | R | `restore --post-restore-scripts` | `DB_DUMP_POST_RESTORE_SCRIPTS` | `dump.post-restore-scripts` | in container, `/scripts.d/post-restore/` |
92+
| directory with scripts to execute before restore | R | `restore --pre-restore-scripts` | `DB_DUMP_PRE_RESTORE_SCRIPTS` | `restore.pre-restore-scripts` | in container, `/scripts.d/pre-restore/` |
93+
| directory with scripts to execute after restore | R | `restore --post-restore-scripts` | `DB_DUMP_POST_RESTORE_SCRIPTS` | `restore.post-restore-scripts` | in container, `/scripts.d/post-restore/` |
9494
| retention policy for backups | BP | `dump --retention` | `RETENTION` | `prune.retention` | Infinite |
95+
96+
## Configuration File
97+
98+
### Format
99+
100+
The config file is a YAML file. You can write the yaml configuration file by hand. Alternatively, you can use an online service
101+
to generate one for you. Referenced services will be listed here in the future.
102+
103+
The keys are:
104+
105+
* `version`: the version of configuration, must be `config.databack.io/v1`
106+
* `kind`: the kind of configuration, must be one of:
107+
* `local`: local configuration
108+
* `remote`: remote configuration
109+
* `metadata`: metadata about the configuration. Not required. Used primarily for validating or optional information.
110+
* `name` (optional): the name of the configuration
111+
* `description` (optional): a description of the configuration
112+
* `digest` (optional): the digest of the configuration, excluding the `digest` key itself. Everything else, including optional metadata, is included.
113+
* `created` (optional): the date the configuration was created in [ISO8601 date format](https://en.wikipedia.org/wiki/ISO_8601), e.g. `2021-01-01T00:00:00Z`. The timezone always should be `Z` for UTC.
114+
* `spec`: the specification. Varies by the `kind` of configuration.
115+
116+
The contents of `spec` depend on the kind of configuration.
117+
118+
#### Local Configuration
119+
120+
For local configuration, the `spec` is composed of the following. See the [Configuration Options](#configuration-options)
121+
for details of each.
122+
123+
* `dump`: the dump configuration
124+
* `include`: list of tables to include
125+
* `exclude`: list of tables to exclude
126+
* `safechars`: safe characters in filename
127+
* `no-database-name`: remove `USE <database>` from dumpfile
128+
* `schedule`: the schedule configuration
129+
* `frequency`: the frequency of the schedule
130+
* `begin`: the time to begin the schedule
131+
* `cron`: the cron schedule
132+
* `once`: run once and exit
133+
* `compression`: the compression to use
134+
* `compact`: compact the dump
135+
* `max-allowed-packet`: max packet size
136+
* `filename-pattern`: the filename pattern
137+
* `scripts`:
138+
* `pre-backup`: path to directory with pre-backup scripts
139+
* `post-backup`: path to directory with post-backup scripts
140+
* `targets`: list of names of known targets, defined in the `targets` section, where to save the backup
141+
* `restore`: the restore configuration
142+
* `scripts`:
143+
* `pre-restore`: path to directory with pre-restore scripts
144+
* `post-restore`: path to directory with post-restore scripts
145+
* `database`: the database configuration
146+
* `server`: host:port
147+
* `port`: port (deprecated)
148+
* `credentials`: access credentials for the database
149+
* `username`: user
150+
* `password`: password
151+
* `prune`: the prune configuration
152+
* `retention`: retention policy
153+
* `targets`: target configurations, each of which can be reference by other sections. Key is the name of the target that is referenced elsewhere. Each one has the following structure:
154+
* `type`: the type of target, one of: file, s3, smb
155+
* `url`: the URL of the target
156+
* `details`: access details for the target, depends on target type:
157+
* Type s3:
158+
* `region`: the region
159+
* `endpoint`: the endpoint
160+
* `access-key-id`: the access key ID (s3)
161+
* `secret-access-key`: the secret access key (s3)
162+
* Type smb:
163+
* `domain`: the domain (smb)
164+
* `username`: the username (smb)
165+
* `password`: the password (smb)
166+
* `logging`: the log level, one of: error,warning,info,debug,trace; default is info
167+
* `telemetry`: configuration for sending telemetry data (optional)
168+
* `url`: URL to telemetry service
169+
* `certificate`: the certificate for the telemetry server or a CA that signed the server's TLS certificate. Not required if telemetry server does not use TLS, or if the system's certificate store already contains the server's cert or CA.
170+
* `credentials`: unique token provided by the remote service as credentials, base64-encoded
171+
172+
#### Remote Configuration
173+
174+
For remote configuration, the `spec` is composed of the following:
175+
176+
* `url`: the URL of the remote configuration; required
177+
* `certificate`: the certificate for the server or a CA that signed the server's TLS certificate. Not required if remote server does not use TLS, or if the system's certificate store already contains the server's cert or CA.
178+
* `credentials`: unique token provided by the remote service as credentials, base64-encoded
179+
180+
The configuration file retrieved from a remote **always** has the same structure as any config file. It even can be
181+
saved locally and used as a local configuration. This means it also can
182+
reference another remote configuration, just like a local one. That can in turn reference another
183+
and so on, ad infinitum. In practice, remote service will avoid this.
184+
185+
### Multiple Configurations
186+
187+
As of version 1.0 of `mysql-backup`, there is support only for one config file. This means:
188+
189+
* The `--config` flag can be used only once.
190+
* The config file does not support [multiple yaml documents in a single file](https://yaml.org/spec/1.2.2/). If you ask it to read a yaml file with multiple documents sepaarted by `---`, it will read only the first one.
191+
* You can have chaining, as described in the [remote configuration](#remote-configuration) section, where one file of kind `remote` references another, which itself is `remote`, etc. But only the final one will be used. It is not merging.

docs/security.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Security
2+
3+
## Database and Targets
4+
5+
`mysql-backup` uses standard libraries for accessing remote services, including the database to backup
6+
or restore, and targets for saving backups, restoring backups, or pruning.
7+
8+
## Logs
9+
10+
Logs never should include credentials or other secrets, including at the most detailed level like `trace`. If, despite our efforts,
11+
you see confidential information in logs, please report an issue immediately.
12+
13+
## Telemetry
14+
15+
Remote telemetry services store your logs, as well as details about when backups occurred, if there were any errors,
16+
and how long they took. This means that the telemetry services knows:
17+
18+
* The names of the databases you back up
19+
* The names of the targets you use
20+
* The times of backups
21+
* The duration of backups
22+
* The success or failure of backups
23+
* Backup logs. As described above in [Logs](#logs), logs never should include credentials or other secrets.
24+
25+
Telemetry services do not store your credentials or other secrets, nor do they store the contents of your backups.
26+
They _do_ know the names of your database tables, as those appear in the logs.
27+
28+
## Remote Configuration
29+
30+
Remote configuration services store your configuration, including the names of your databases and targets, as well as
31+
credentials. However, they only have that data encrypted in a way that only you can decrypt. When you load configuration
32+
into the remote service, it is encrypted locally to you, and then stored as an encrypted blob. The remote service never
33+
sees your unencrypted data.
34+
35+
The data is decrypted by `mysql-backup` locally on your machine, when you retrieve the configuration.
36+
37+
Your access token to the remote service, stored in your local configuration file, is a
38+
[Curve25519 private key](https://en.wikipedia.org/wiki/Curve25519), which authenticates
39+
you to the remote service. The remote service never sees this key, only the public key, which is used to verify your identity.
40+
41+
This key is then used to decrypt the configuration blob, which is used to configure `mysql-backup`.
42+
43+
In configuration files, the key is stored base64-encoded.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ require (
2222
)
2323

2424
require (
25+
github.com/aws/aws-sdk-go-v2/credentials v1.13.29
2526
github.com/cloudsoda/go-smb2 v0.0.0-20231106205947-b0758ecc4c67
27+
github.com/dsnet/compress v0.0.1
2628
github.com/go-test/deep v1.1.0
2729
)
2830

2931
require (
3032
github.com/Microsoft/go-winio v0.6.1 // indirect
3133
github.com/aws/aws-sdk-go v1.44.256 // indirect
3234
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
33-
github.com/aws/aws-sdk-go-v2/credentials v1.13.29 // indirect
3435
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.6 // indirect
3536
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.36 // indirect
3637
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.30 // indirect
@@ -48,7 +49,6 @@ require (
4849
github.com/davecgh/go-spew v1.1.1 // indirect
4950
github.com/docker/distribution v2.8.2+incompatible // indirect
5051
github.com/docker/go-units v0.5.0 // indirect
51-
github.com/dsnet/compress v0.0.1 // indirect
5252
github.com/fsnotify/fsnotify v1.6.0 // indirect
5353
github.com/geoffgarside/ber v1.1.0 // indirect
5454
github.com/gogo/protobuf v1.3.2 // indirect

0 commit comments

Comments
 (0)