Skip to content
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
36 changes: 33 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type options struct {
NoConfigFile bool
BinDirectory string
Directory string
Mode int
Host string
Port int
Username string
Expand Down Expand Up @@ -129,6 +130,7 @@ func defaultOptions() options {
return options{
NoConfigFile: false,
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -161,6 +163,17 @@ func (*parseCliResult) Error() string {
return "please exit now"
}

func validateMode(s string) (int, error) {
if (strings.HasPrefix(s, "0") && len(s) <= 5) || (strings.HasPrefix(s, "-")) {
mode, err := strconv.ParseInt(s, 0, 32)
if err != nil {
return 0, fmt.Errorf("Invalid permission %q", s)
}
return int(mode), nil
}
return 0, fmt.Errorf("Invalid permission %q, must be octal (start by 0 and max 5 digits) number or negative", s)
}

func validateDumpFormat(s string) error {
for _, format := range []string{"plain", "custom", "tar", "directory"} {
// PostgreSQL tools allow the full name of the format and the
Expand Down Expand Up @@ -251,7 +264,7 @@ func validateDirectory(s string) error {
}

func parseCli(args []string) (options, []string, error) {
var format, purgeKeep, purgeInterval string
var format, mode, purgeKeep, purgeInterval string

opts := defaultOptions()
pce := &parseCliResult{}
Expand All @@ -268,6 +281,7 @@ func parseCli(args []string) (options, []string, error) {
pflag.BoolVar(&opts.NoConfigFile, "no-config-file", false, "skip reading config file\n")
pflag.StringVarP(&opts.BinDirectory, "bin-directory", "B", "", "PostgreSQL binaries directory. Empty to search $PATH")
pflag.StringVarP(&opts.Directory, "backup-directory", "b", "/var/backups/postgresql", "store dump files there")
pflag.StringVarP(&mode, "backup-file-mode", "m", "0600", "mode to apply to dump files")
pflag.StringVarP(&opts.CfgFile, "config", "c", defaultCfgFile, "alternate config file")
pflag.StringSliceVarP(&opts.ExcludeDbs, "exclude-dbs", "D", []string{}, "list of databases to exclude")
pflag.BoolVarP(&opts.WithTemplates, "with-templates", "t", false, "include templates")
Expand Down Expand Up @@ -414,6 +428,12 @@ func parseCli(args []string) (options, []string, error) {
changed = append(changed, "include-dbs")
}

parsed_mode, err := validateMode(mode)
if err != nil {
return opts, changed, fmt.Errorf("invalid value for --backup-file-mode: %s", err)
}
opts.Mode = parsed_mode

// Validate purge keep and time limit
keep, err := validatePurgeKeepValue(purgeKeep)
if err != nil {
Expand Down Expand Up @@ -520,7 +540,7 @@ func validateConfigurationFile(cfg *ini.File) error {
s, _ := cfg.GetSection(ini.DefaultSection)

known_globals := []string{
"bin_directory", "backup_directory", "timestamp_format", "host", "port", "user",
"bin_directory", "backup_directory", "backup_file_mode", "timestamp_format", "host", "port", "user",
"dbname", "exclude_dbs", "include_dbs", "with_templates", "format",
"parallel_backup_jobs", "compress_level", "jobs", "pause_timeout",
"purge_older_than", "purge_min_keep", "checksum_algorithm", "pre_backup_hook",
Expand Down Expand Up @@ -574,7 +594,7 @@ gkLoop:
}

func loadConfigurationFile(path string) (options, error) {
var format, purgeKeep, purgeInterval string
var format, mode, purgeKeep, purgeInterval string

opts := defaultOptions()

Expand All @@ -600,6 +620,7 @@ func loadConfigurationFile(path string) (options, error) {
// flags
opts.BinDirectory = s.Key("bin_directory").MustString("")
opts.Directory = s.Key("backup_directory").MustString("/var/backups/postgresql")
mode = s.Key("backup_file_mode").MustString("0600")
timeFormat := s.Key("timestamp_format").MustString("rfc3339")
opts.Host = s.Key("host").MustString("")
opts.Port = s.Key("port").MustInt(0)
Expand Down Expand Up @@ -662,6 +683,13 @@ func loadConfigurationFile(path string) (options, error) {
opts.AzureKey = s.Key("azure_key").MustString("")
opts.AzureEndpoint = s.Key("azure_endpoint").MustString("blob.core.windows.net")

// Validate mode and convert to int
m, err := validateMode(mode)
if err != nil {
return opts, err
}
opts.Mode = m

// Validate purge keep and time limit
keep, err := validatePurgeKeepValue(purgeKeep)
if err != nil {
Expand Down Expand Up @@ -811,6 +839,8 @@ func mergeCliAndConfigOptions(cliOpts options, configOpts options, onCli []strin
opts.BinDirectory = cliOpts.BinDirectory
case "backup-directory":
opts.Directory = cliOpts.Directory
case "backup-file-mode":
opts.Mode = cliOpts.Mode
case "exclude-dbs":
opts.ExcludeDbs = cliOpts.ExcludeDbs
case "include-dbs":
Expand Down
48 changes: 46 additions & 2 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ func TestValidateDumpFormat(t *testing.T) {

}

func TestValidateMode(t *testing.T) {
var tests = []struct {
give string
want int
wantError bool
}{
{"0700", 448, false},
{"070000", 0, true}, // invalid mode (too long)
{"18446744000", 0, true}, // still invalid, positive integer
{"08170", 0, true}, // non valid mode (8 on it)
{"-8170", -8170, false}, // valid and mean do nothing (useful when using umask)
}

l.logger.SetOutput(ioutil.Discard)
for i, st := range tests {
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
got, err := validateMode(st.give)
if err == nil && st.wantError {
t.Errorf("excepted an error got nil")
} else if err != nil && !st.wantError {
t.Errorf("did not want an error, got %s", err)
}
if got != st.want {
t.Errorf("got %v, want %v", got, st.want)
}
})
}
}

func TestValidatePurgeKeepValue(t *testing.T) {
var tests = []struct {
give string
Expand Down Expand Up @@ -183,6 +212,7 @@ func TestDefaultOptions(t *testing.T) {

var want = options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -228,6 +258,7 @@ func TestParseCli(t *testing.T) {
[]string{"-b", "test", "-Z", "2", "a", "b"},
options{
Directory: "test",
Mode: 0o600,
Dbnames: []string{"a", "b"},
Format: 'c',
DirJobs: 1,
Expand Down Expand Up @@ -255,6 +286,7 @@ func TestParseCli(t *testing.T) {
[]string{"-t", "--without-templates"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
WithTemplates: false,
Format: 'c',
DirJobs: 1,
Expand Down Expand Up @@ -306,6 +338,7 @@ func TestParseCli(t *testing.T) {
[]string{"--upload", "wrong"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -334,6 +367,7 @@ func TestParseCli(t *testing.T) {
[]string{"--download", "wrong"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -370,6 +404,7 @@ func TestParseCli(t *testing.T) {
[]string{"--cipher-pass", "mypass"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -398,6 +433,7 @@ func TestParseCli(t *testing.T) {
[]string{"--cipher-private-key", "mykey"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -426,6 +462,7 @@ func TestParseCli(t *testing.T) {
[]string{"--cipher-public-key", "fakepubkey"},
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -538,10 +575,11 @@ func TestLoadConfigurationFile(t *testing.T) {
want options
}{
{
[]string{"backup_directory = test", "port = 5433"},
[]string{"backup_directory = test", "port = 5433", "backup_file_mode = 0700"},
false,
options{
Directory: "test",
Mode: 0o700,
Port: 5433,
Format: 'c',
DirJobs: 1,
Expand All @@ -562,10 +600,11 @@ func TestLoadConfigurationFile(t *testing.T) {
},
},
{ // ensure comma separated lists work
[]string{"backup_directory = test", "include_dbs = a, b, postgres", "compress_level = 9"},
[]string{"backup_directory = test", "include_dbs = a, b, postgres", "compress_level = 9", "backup_file_mode = 0400"},
false,
options{
Directory: "test",
Mode: 0o400,
Dbnames: []string{"a", "b", "postgres"},
Format: 'c',
DirJobs: 1,
Expand All @@ -590,6 +629,7 @@ func TestLoadConfigurationFile(t *testing.T) {
false,
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand All @@ -613,6 +653,7 @@ func TestLoadConfigurationFile(t *testing.T) {
false,
options{
Directory: "/var/backups/postgresql",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -654,6 +695,7 @@ func TestLoadConfigurationFile(t *testing.T) {
false,
options{
Directory: "test",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: -1,
Expand Down Expand Up @@ -698,6 +740,7 @@ func TestLoadConfigurationFile(t *testing.T) {
false,
options{
Directory: "test",
Mode: 0o600,
Format: 'c',
DirJobs: 1,
CompressLevel: 3,
Expand Down Expand Up @@ -774,6 +817,7 @@ func TestMergeCliAndConfigoptions(t *testing.T) {
want := options{
BinDirectory: "/bin",
Directory: "test",
Mode: 0o600,
Host: "localhost",
Port: 5433,
Username: "test",
Expand Down
8 changes: 6 additions & 2 deletions crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func ageDecryptInternal(src io.Reader, dst io.Writer, identity age.Identity) err
return nil
}

func encryptFile(path string, params encryptParams, keep bool) ([]string, error) {
func encryptFile(path string, mode int, params encryptParams, keep bool) ([]string, error) {
encrypted := make([]string, 0)

i, err := os.Stat(path)
Expand Down Expand Up @@ -210,7 +210,11 @@ func encryptFile(path string, params encryptParams, keep bool) ([]string, error)
}

encrypted = append(encrypted, dstFile)

if mode > 0 {
if err := os.Chmod(dstFile, os.FileMode(mode)); err != nil {
return encrypted, fmt.Errorf("could not chmod to more secure permission for encrypted file: %w", err)
}
}
if !keep {
l.Verboseln("removing source file:", path)
src.Close()
Expand Down
16 changes: 14 additions & 2 deletions hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func computeChecksum(path string, h hash.Hash) (string, error) {
return string(h.Sum(nil)), nil
}

func checksumFile(path string, algo string) (string, error) {
func checksumFile(path string, mode int, algo string) (string, error) {
var h hash.Hash

switch algo {
Expand Down Expand Up @@ -114,10 +114,16 @@ func checksumFile(path string, algo string) (string, error) {
r, _ := computeChecksum(path, h)
fmt.Fprintf(o, "%x %s\n", r, path)
}
l.Verboseln("computing checksum with MODE", mode, path)
if mode > 0 {
if err := os.Chmod(o.Name(), os.FileMode(mode)); err != nil {
return "", fmt.Errorf("could not chmod checksum file %s: %s", path, err)
}
}
return sumFile, nil
}

func checksumFileList(paths []string, algo string, sumFilePrefix string) (string, error) {
func checksumFileList(paths []string, mode int, algo string, sumFilePrefix string) (string, error) {
var h hash.Hash

switch algo {
Expand Down Expand Up @@ -157,6 +163,12 @@ func checksumFileList(paths []string, algo string, sumFilePrefix string) (string
}

fmt.Fprintf(o, "%x *%s\n", r, path)

if mode > 0 {
if err := os.Chmod(o.Name(), os.FileMode(mode)); err != nil {
return "", fmt.Errorf("could not chmod checksum file %s: %s", path, err)
}
}
}

if failed {
Expand Down
12 changes: 6 additions & 6 deletions hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,18 @@ func TestChecksumFile(t *testing.T) {
}

// bad algo
if _, err := checksumFile("", "none"); err != nil {
if _, err := checksumFile("", 0o700, "none"); err != nil {
t.Errorf("expected <nil>, got %q\n", err)
}

if _, err := checksumFile("", "other"); err == nil {
if _, err := checksumFile("", 0o700, "other"); err == nil {
t.Errorf("expected err, got <nil>\n")
}

// test each algo with the file
for i, st := range tests {
t.Run(fmt.Sprintf("f%v", i), func(t *testing.T) {
if _, err := checksumFile("test", st.algo); err != nil {
if _, err := checksumFile("test", 0o700, st.algo); err != nil {
t.Errorf("checksumFile returned: %v", err)
}

Expand All @@ -111,12 +111,12 @@ func TestChecksumFile(t *testing.T) {
// bad files
var e *os.PathError
l.logger.SetOutput(ioutil.Discard)
if _, err := checksumFile("", "sha1"); !errors.As(err, &e) {
if _, err := checksumFile("", 0o700, "sha1"); !errors.As(err, &e) {
t.Errorf("expected an *os.PathError, got %q\n", err)
}

os.Chmod("test.sha1", 0444)
if _, err := checksumFile("test", "sha1"); !errors.As(err, &e) {
if _, err := checksumFile("test", 0o700, "sha1"); !errors.As(err, &e) {
t.Errorf("expected an *os.PathError, got %q\n", err)
}
os.Chmod("test.sha1", 0644)
Expand All @@ -138,7 +138,7 @@ func TestChecksumFile(t *testing.T) {
// test each algo with the directory
for i, st := range tests {
t.Run(fmt.Sprintf("d%v", i), func(t *testing.T) {
if _, err := checksumFile("test.d", st.algo); err != nil {
if _, err := checksumFile("test.d", 0o700, st.algo); err != nil {
t.Errorf("checksumFile returned: %v", err)
}

Expand Down
Loading