Commit bb885562 authored by ale's avatar ale
Browse files

Use --repository-file to avoid leaking passwords in logs

Fixes issue #3.
parent dbf3ccd1
Pipeline #24120 failed with stages
in 36 seconds
......@@ -5,7 +5,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
......@@ -21,22 +20,32 @@ import (
)
type resticRepository struct {
bin string
bin string
uri string
passwordFile string
excludes []string
excludeFiles []string
autoPrune bool
tmpDir string
passwordFile string
repositoryFile string
excludes []string
excludeFiles []string
autoPrune bool
cacheMgr cacheManager
initialized sync.Once
}
func (r *resticRepository) resticCmd() string {
args := []string{r.bin, "-r", r.uri}
func (r *resticRepository) resticCmd(cmd string) string {
args := []string{r.bin, cmd}
if r.repositoryFile != "" {
args = append(args, "--repository-file", r.repositoryFile)
}
if r.passwordFile != "" {
args = append(args, "--password-file", r.passwordFile)
}
return strings.Join(args, " ")
}
func (r *resticRepository) excludeArgs() string {
var args []string
for _, x := range r.excludes {
args = append(args, "--exclude", x)
}
......@@ -93,16 +102,18 @@ func newResticRepository(params Params) (Repository, error) {
return nil, err
}
tmpf, err := ioutil.TempFile("", "restic-pw-")
// Create a temporary directory and write repository URL and
// password in files therein.
tmpDir, err := ioutil.TempDir("", "tabacco-restic-")
if err != nil {
return nil, err
}
if _, err := io.WriteString(tmpf, password); err != nil {
os.Remove(tmpf.Name()) // nolint
passwordFile := filepath.Join(tmpDir, "pw")
if err := ioutil.WriteFile(passwordFile, []byte(password), 0600); err != nil {
return nil, err
}
if err := tmpf.Close(); err != nil {
os.Remove(tmpf.Name()) // nolint
repositoryFile := filepath.Join(tmpDir, "repo")
if err := ioutil.WriteFile(repositoryFile, []byte(uri), 0600); err != nil {
return nil, err
}
......@@ -115,19 +126,22 @@ func newResticRepository(params Params) (Repository, error) {
if b, ok := params.GetBool("autoprune"); ok {
autoPrune = b
}
return &resticRepository{
bin: bin,
uri: uri,
passwordFile: tmpf.Name(),
excludes: params.GetList("exclude"),
excludeFiles: params.GetList("exclude_files"),
autoPrune: autoPrune,
bin: bin,
uri: uri,
tmpDir: tmpDir,
passwordFile: passwordFile,
repositoryFile: repositoryFile,
excludes: params.GetList("exclude"),
excludeFiles: params.GetList("exclude_files"),
autoPrune: autoPrune,
cacheMgr: cmgr,
}, nil
}
func (r *resticRepository) Close() error {
return os.Remove(r.passwordFile)
return os.RemoveAll(r.tmpDir)
}
func (r *resticRepository) Init(ctx context.Context, rctx RuntimeContext) error {
......@@ -135,8 +149,8 @@ func (r *resticRepository) Init(ctx context.Context, rctx RuntimeContext) error
// Restic init will fail if the repository is already
// initialized, ignore errors (but log them).
if err := rctx.Shell().Run(ctx, fmt.Sprintf(
"%s init --quiet || true",
r.resticCmd(),
"%s --quiet || true",
r.resticCmd("init"),
)); err != nil {
GetLogger(ctx).Printf("restic repository init failed (likely harmless): %v", err)
}
......@@ -149,8 +163,8 @@ func (r *resticRepository) Prepare(ctx context.Context, rctx RuntimeContext, bac
return nil
}
return rctx.Shell().Run(ctx, fmt.Sprintf(
"%s forget --host %s --keep-last 10 --prune",
r.resticCmd(),
"%s --host %s --keep-last 10 --prune",
r.resticCmd("forget"),
backup.Host,
))
}
......@@ -161,8 +175,8 @@ func resticBackupTags(backup *Backup, ds *Dataset) string {
func (r *resticRepository) backupCmd(cc cache, backup *Backup, ds *Dataset, inputFile string, exclude []string) string {
cmd := fmt.Sprintf(
"%s backup %s --json --exclude-caches --one-file-system %s --files-from %s",
r.resticCmd(),
"%s %s --json --exclude-caches --one-file-system %s --files-from %s",
r.resticCmd("backup"),
cc.Args(),
resticBackupTags(backup, ds),
inputFile,
......@@ -180,8 +194,8 @@ func (r *resticRepository) getSnapshotID(ctx context.Context, cc cache, shell *S
// Legacy compatibility: query restic using the dataset ID.
data, err := shell.Output(ctx, fmt.Sprintf(
"%s snapshots %s --no-lock --json %s",
r.resticCmd(),
"%s %s --no-lock --json %s",
r.resticCmd("snapshots"),
cc.Args(),
resticBackupTags(backup, ds),
))
......@@ -205,7 +219,7 @@ func (r *resticRepository) restoreCmd(ctx context.Context, cc cache, shell *Shel
}
cmd := []string{
fmt.Sprintf("%s restore", r.resticCmd()),
r.resticCmd("restore"),
}
for _, path := range paths {
......@@ -239,8 +253,8 @@ func (r *resticRepository) backupStreamCmd(cc cache, backup *Backup, ds *Dataset
fakePath := datasetStdinPath(ds)
return fmt.Sprintf(
// The --force prevents restic from trying to find a previous snapshot.
"%s backup %s %s --json --force --stdin --stdin-filename %s",
r.resticCmd(),
"%s %s %s --json --force --stdin --stdin-filename %s",
r.resticCmd("backup"),
cc.Args(),
resticBackupTags(backup, ds),
fakePath,
......@@ -258,8 +272,8 @@ func (r *resticRepository) restoreStreamCmd(ctx context.Context, cc cache, shell
// Restore the file to a temporary directory, then pipe it.
return fmt.Sprintf(
"(%s restore %s --target %s %s 1>&2 && cat %s)",
r.resticCmd(),
"(%s %s --target %s %s 1>&2 && cat %s)",
r.resticCmd("restore"),
cc.Args(),
target,
snap,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment