diff --git a/Dockerfile.in b/Dockerfile.in index 7ebdcf5..59c2061 100644 --- a/Dockerfile.in +++ b/Dockerfile.in @@ -22,7 +22,11 @@ RUN apt-get update \ openssh-client \ && rm -rf /var/lib/apt/lists/* +# By default we will run as this user... RUN echo "git-sync:x:65533:65533::/tmp:/sbin/nologin" >> /etc/passwd +# ...but the user might choose a different UID and pass --add-user +# which needs to be able to write to /etc/passwd. +RUN chmod 0666 /etc/passwd ADD bin/{ARG_OS}_{ARG_ARCH}/{ARG_BIN} /{ARG_BIN} diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index e8ec321..60c9b70 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -94,6 +94,8 @@ var flSSHKnownHosts = flag.Bool("ssh-known-hosts", envBool("GIT_KNOWN_HOSTS", tr "enable SSH known_hosts verification") var flSSHKnownHostsFile = flag.String("ssh-known-hosts-file", envString("GIT_SSH_KNOWN_HOSTS_FILE", "/etc/git-secret/known_hosts"), "the known_hosts file to use") +var flAddUser = flag.Bool("add-user", envBool("GIT_SYNC_ADD_USER", false), + "add a record to /etc/passwd for the current UID/GID (needed to use SSH with a different UID)") var flCookieFile = flag.Bool("cookie-file", envBool("GIT_COOKIE_FILE", false), "use git cookiefile") @@ -241,6 +243,13 @@ func main() { os.Exit(1) } + if *flAddUser { + if err := addUser(); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: can't write to /etc/passwd: %v\n", err) + os.Exit(1) + } + } + // This context is used only for git credentials initialization. There are no long-running operations like // `git clone`, so initTimeout set to 30 seconds should be enough. ctx, cancel := context.WithTimeout(context.Background(), initTimeout) @@ -390,6 +399,29 @@ func sleepForever() { os.Exit(0) } +// Put the current UID/GID into /etc/passwd so SSH can look it up. This +// assumes that we have the permissions to write to it. +func addUser() error { + home := os.Getenv("HOME") + if home == "" { + cwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("can't get current working directory: %v", err) + } + home = cwd + } + + f, err := os.OpenFile("/etc/passwd", os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + str := fmt.Sprintf("git-sync:x:%d:%d::%s:/sbin/nologin\n", os.Getuid(), os.Getgid(), home) + _, err = f.WriteString(str) + return err +} + // updateSymlink atomically swaps the symlink to point at the specified // directory and cleans up the previous worktree. If there was a previous // worktree, this returns the path to it. diff --git a/docs/ssh.md b/docs/ssh.md index 8f7519a..e667923 100644 --- a/docs/ssh.md +++ b/docs/ssh.md @@ -103,6 +103,11 @@ that this is a Pod-wide setting, unlike the container `securityContext` above. # ... ``` +If you want git-sync to run as a different (non-root) UID and GID, you can +change these last blocks to any UID/GID you like. SSH demands that the current +UID be present in /etc/passwd, so in this case you will need to add the +`--add-user` flag to git-sync's args array. + **Note:** Kubernetes mounts the Secret with permissions 0444 by default (not restrictive enough to be used as an SSH key), so make sure you set the `defaultMode`.