Add a new authentiate method GIT_SYNC_AUTH_URL.

It specifies a HTTP URL which will return username&password which will
be used to authenticate access to the git repo.

This is mainly used for git repo accecpt dynamic password (for example
oauth bare token). Because the dynamic password might expire very soon,
so it's added to the main syncRepo loop.

Typical usage case is work with a sidecar called gce-node-auth on GKE,
it uses the GCE service account's oauth token as password to access
Cloud Source Repo.

Please see the repo below for how it worked.
https://github.com/cydu-cloud/gce-node-auth/blob/master/git-sync-with-gce-node-auth.yaml
This commit is contained in:
Chuanying Du 2019-12-16 23:45:37 -08:00
parent a2a8f72733
commit 9cae624f8c
3 changed files with 112 additions and 5 deletions

View File

@ -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
}

38
docs/auth-url.md Normal file
View File

@ -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",
},
...
]
}
```

View File

@ -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