diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index f21ab1a..5c9f8a6 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -98,6 +98,9 @@ var flSSHKnownHostsFile = flag.String("ssh-known-hosts-file", envString("GIT_SSH var flCookieFile = flag.Bool("cookie-file", envBool("GIT_COOKIE_FILE", false), "use git cookiefile") +var flAuthURL = flag.String("auth-url", envString("GIT_SYNC_AUTH_URL", ""), + "the URL for git auth callback") + var flGitCmd = flag.String("git", envString("GIT_SYNC_GIT", "git"), "the git command to run (subject to PATH search, mostly for testing)") @@ -233,8 +236,8 @@ func main() { os.Exit(1) } - if (*flUsername != "" || *flPassword != "" || *flCookieFile) && *flSSH { - fmt.Fprintf(os.Stderr, "ERROR: --ssh is set but --username, --password, or --cookie-file were provided\n") + if (*flUsername != "" || *flPassword != "" || *flCookieFile || *flAuthURL != "") && *flSSH { + fmt.Fprintf(os.Stderr, "ERROR: --ssh is set but --username, --password, --auth-url, or --cookie-file were provided\n") os.Exit(1) } @@ -263,6 +266,13 @@ func main() { } } + if *flAuthURL != "" { + if err := setupGitAuthURL(ctx); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: can't set auth callback url: %v\n", err) + os.Exit(1) + } + } + // The scope of the initialization context ends here, so we call cancel to release resources associated with it. cancel() @@ -315,7 +325,7 @@ func main() { for { start := time.Now() ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(*flSyncTimeout)) - if changed, hash, err := syncRepo(ctx, *flRepo, *flBranch, *flRev, *flDepth, *flRoot, *flDest); err != nil { + if changed, hash, err := syncRepo(ctx, *flRepo, *flBranch, *flRev, *flDepth, *flRoot, *flDest, *flAuthURL); err != nil { syncDuration.WithLabelValues("error").Observe(time.Since(start).Seconds()) syncCount.WithLabelValues("error").Inc() if *flMaxSyncFailures != -1 && failCount >= *flMaxSyncFailures { @@ -571,7 +581,15 @@ func revIsHash(ctx context.Context, rev, gitRoot string) (bool, error) { // syncRepo syncs the branch of a given repository to the destination at the given rev. // returns (1) whether a change occured, (2) the new hash, and (3) an error if one happened -func syncRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot, dest string) (bool, string, error) { +func syncRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot, dest string, authUrl string) (bool, string, error) { + if authUrl != "" { + // For Auth Callback URL, the credentials behind is dynamic, it needs to be + // re-fetched each time. + if err := setupGitAuthURL(ctx); err != nil { + return false, "", fmt.Errorf("can't set auth callback url: %v", err) + } + } + target := path.Join(gitRoot, dest) gitRepoPath := path.Join(target, ".git") var hash string @@ -756,3 +774,54 @@ func setupGitCookieFile(ctx context.Context) error { return nil } + +// The expected output of the auth URL are: +// username=xxx@example.com +// password=ya29.xxxyyyzzz +func setupGitAuthURL(ctx context.Context) error { + log.V(1).Info("configuring auth callback URL") + + var netClient = &http.Client{ + Timeout: time.Second * 1, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + } + httpReq, err := http.NewRequestWithContext(ctx, "GET", *flAuthURL, nil) + if err != nil { + return fmt.Errorf("error create auth request: %v", err) + } + resp, err := netClient.Do(httpReq) + if err != nil { + return fmt.Errorf("error access auth url: %v", err) + } + if resp.StatusCode != 200 { + return fmt.Errorf("access auth url: %v", err) + } + authData, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return fmt.Errorf("error read auth response: %v", err) + } + + username := "" + password := "" + for _, line := range strings.Split(string(authData), "\n") { + keyValues := strings.SplitN(line, "=", 2) + if len(keyValues) != 2 { + continue + } + switch keyValues[0] { + case "username": + username = keyValues[1] + case "password": + password = keyValues[1] + } + } + + if err := setupGitAuth(ctx, username, password, *flRepo); err != nil { + return fmt.Errorf("error setup git auth: %v", err) + } + + return nil +} diff --git a/docs/auth-url.md b/docs/auth-url.md new file mode 100644 index 0000000..8ff5704 --- /dev/null +++ b/docs/auth-url.md @@ -0,0 +1,38 @@ +# Using an Http Auth URL with git-sync + +# Step 1: Create Auth Service + +First, create a http service which can provide the username and password for the +git repo. + +Example of the auth url output: + +``` +username=xxx@example.com +password=ya29.xxxxyyyyzzzz +``` + +# Step 2: Configure git-sync container + +In your git-sync container configuration, specify the auth url. + +The credentials will pass in plain text, make sure the connection between git-sync +and auth server are secure. The recommended way is the auth server running within +the same pod as git-sync. + +``` +{ + name: "git-sync", + ... + env: [ + { + name: "GIT_SYNC_REPO", + value: "https://source.developers.google.com/p/[GCP PROJECT ID]/r/[REPO NAME]" + }, { + name: "GIT_SYNC_AUTH_URL", + value: "http://localhost:8080/gce_node_auth", + }, + ... + ] +} +``` diff --git a/docs/cookie-file.md b/docs/cookie-file.md index 3b14bbb..e06408d 100644 --- a/docs/cookie-file.md +++ b/docs/cookie-file.md @@ -34,7 +34,7 @@ volumes: [ ], ``` -# Step 2: Configure git-sync container +# Step 3: Configure git-sync container In your git-sync container configuration, mount your volume at "/etc/git-secret". Make sure to pass the `--cookie-file` flag or set the