From 7a5068446afa6e8940ca59f38701cbcc4a70fe72 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 10 Dec 2021 10:18:52 +0100 Subject: [PATCH] Add test case for ownership --- Dockerfile | 2 +- cmd/backup/main.go | 29 ++++++++++++++++++++++++++--- test/compose/run.sh | 7 +++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index c17ddef..4c3a63e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ FROM alpine:3.14 WORKDIR /root -RUN apk add --update ca-certificates +RUN apk add --update ca-certificates sudo COPY --from=builder /app/backup /usr/bin/backup diff --git a/cmd/backup/main.go b/cmd/backup/main.go index b7a893d..6bd0def 100644 --- a/cmd/backup/main.go +++ b/cmd/backup/main.go @@ -10,9 +10,11 @@ import ( "fmt" "io" "os" + "os/exec" "path" "path/filepath" "strings" + "syscall" "time" "github.com/docker/docker/api/types" @@ -447,7 +449,7 @@ func (s *script) copyBackup() error { if err := os.Chown(s.file, s.c.BackupUID, s.c.BackupGID); err != nil { return fmt.Errorf("copyBackup: error changing owner on temp file: %w", err) } - if err := copyFile(s.file, path.Join(s.c.BackupArchive, name)); err != nil { + if err := copyFile(s.file, path.Join(s.c.BackupArchive, name), s.c.BackupGID, s.c.BackupUID); err != nil { return fmt.Errorf("copyBackup: error copying file to local archive: %w", err) } s.logger.Infof("Stored copy of backup `%s` in local archive `%s`.", s.file, s.c.BackupArchive) @@ -686,14 +688,14 @@ func lock(lockfile string) func() error { } // copy creates a copy of the file located at `dst` at `src`. -func copyFile(src, dst string) error { +func copyFile(src, dst string, uid, gid int) error { in, err := os.Open(src) if err != nil { return fmt.Errorf("copyFile: error opening source file: %w", err) } defer in.Close() - out, err := os.Create(dst) + out, err := touch(dst, uid, gid) if err != nil { return fmt.Errorf("copyFile: error creating destination: %w", err) } @@ -706,6 +708,27 @@ func copyFile(src, dst string) error { return out.Close() } +func touch(file string, uid, gid int) (*os.File, error) { + if uid < 1 || gid < 1 { + out, err := os.Create(file) + if err != nil { + return nil, fmt.Errorf("touch: error creating destination: %w", err) + } + return out, nil + } + cmd := exec.Command("sudo", "-u", fmt.Sprintf("%d", uid), "touch", file) + + syscall.Umask(0077) + + cmd.SysProcAttr = &syscall.SysProcAttr{} + cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)} + + if err := cmd.Run(); err != nil { + return nil, fmt.Errorf("touch: error creating destination for owner %d:%d: %w", uid, gid, err) + } + return os.Open(file) +} + // join takes a list of errors and joins them into a single error func join(errs ...error) error { if len(errs) == 1 { diff --git a/test/compose/run.sh b/test/compose/run.sh index 22fe8c7..c093622 100755 --- a/test/compose/run.sh +++ b/test/compose/run.sh @@ -19,6 +19,13 @@ docker run --rm -it \ echo "[TEST:PASS] Found relevant files in untared remote backup." test -L ./local/test.latest.tar.gz.gpg + +owner=$(stat -c '%U:%G' ./local/test.tar.gz.gpg) +if [ "$owner" != "1000:1000" ]; then + echo "[TEST:FAIL] Expected backup file to have correct owners, got $owner" + exit 1 +fi + echo 1234secret | gpg -d --yes --passphrase-fd 0 ./local/test.tar.gz.gpg > ./local/decrypted.tar.gz tar -xf ./local/decrypted.tar.gz -C /tmp && test -f /tmp/backup/app_data/offen.db rm ./local/decrypted.tar.gz