From 9cae624f8c4cc558c079ff9ee2802110eff17d0c Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Mon, 16 Dec 2019 23:45:37 -0800 Subject: [PATCH 01/11] 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 --- cmd/git-sync/main.go | 77 +++++++++++++++++++++++++++++++++++++++++--- docs/auth-url.md | 38 ++++++++++++++++++++++ docs/cookie-file.md | 2 +- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 docs/auth-url.md 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 From 67a0788aa25366b4cb5fb6283050c66df423ae4c Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Tue, 17 Dec 2019 10:48:20 -0800 Subject: [PATCH 02/11] Rename to GIT_ASKPASS_URL and also update related examples. --- cmd/git-sync/main.go | 27 ++++++++++++++------------- docs/auth-url.md | 28 +++++++++++++++------------- docs/cookie-file.md | 14 +++++++------- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index 5c9f8a6..bd64f4d 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -98,8 +98,8 @@ 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 flAskPassURL = flag.String("askpass-url", envString("GIT_ASKPASS_URL", ""), + "the URL for GIT_ASKPASS callback") var flGitCmd = flag.String("git", envString("GIT_SYNC_GIT", "git"), "the git command to run (subject to PATH search, mostly for testing)") @@ -236,7 +236,7 @@ func main() { os.Exit(1) } - if (*flUsername != "" || *flPassword != "" || *flCookieFile || *flAuthURL != "") && *flSSH { + if (*flUsername != "" || *flPassword != "" || *flCookieFile || *flAskPassURL != "") && *flSSH { fmt.Fprintf(os.Stderr, "ERROR: --ssh is set but --username, --password, --auth-url, or --cookie-file were provided\n") os.Exit(1) } @@ -266,9 +266,9 @@ func main() { } } - if *flAuthURL != "" { - if err := setupGitAuthURL(ctx); err != nil { - fmt.Fprintf(os.Stderr, "ERROR: can't set auth callback url: %v\n", err) + if *flAskPassURL != "" { + if err := setupGitAskPassURL(ctx); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: failed to call ASKPASS callback URL: %v\n", err) os.Exit(1) } } @@ -325,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, *flAuthURL); err != nil { + if changed, hash, err := syncRepo(ctx, *flRepo, *flBranch, *flRev, *flDepth, *flRoot, *flDest, *flAskPassURL); err != nil { syncDuration.WithLabelValues("error").Observe(time.Since(start).Seconds()) syncCount.WithLabelValues("error").Inc() if *flMaxSyncFailures != -1 && failCount >= *flMaxSyncFailures { @@ -585,8 +585,8 @@ func syncRepo(ctx context.Context, repo, branch, rev string, depth int, gitRoot, 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) + if err := setupGitAskPassURL(ctx); err != nil { + return false, "", fmt.Errorf("failed to call GIT_ASKPASS_URL: %v", err) } } @@ -775,11 +775,12 @@ func setupGitCookieFile(ctx context.Context) error { return nil } -// The expected output of the auth URL are: +// The expected ASKPASS callback output are below, +// see https://git-scm.com/docs/gitcredentials for more examples: // username=xxx@example.com // password=ya29.xxxyyyzzz -func setupGitAuthURL(ctx context.Context) error { - log.V(1).Info("configuring auth callback URL") +func setupGitAskPassURL(ctx context.Context) error { + log.V(1).Info("configuring GIT_ASKPASS_URL") var netClient = &http.Client{ Timeout: time.Second * 1, @@ -787,7 +788,7 @@ func setupGitAuthURL(ctx context.Context) error { return http.ErrUseLastResponse }, } - httpReq, err := http.NewRequestWithContext(ctx, "GET", *flAuthURL, nil) + httpReq, err := http.NewRequestWithContext(ctx, "GET", *flAskPassURL, nil) if err != nil { return fmt.Errorf("error create auth request: %v", err) } diff --git a/docs/auth-url.md b/docs/auth-url.md index 8ff5704..9f85ae0 100644 --- a/docs/auth-url.md +++ b/docs/auth-url.md @@ -1,26 +1,28 @@ # Using an Http Auth URL with git-sync -# Step 1: Create Auth Service +## Step 1: Create a GIT_ASKPASS HTTP Service -First, create a http service which can provide the username and password for the -git repo. +The GIT ASKPASS Service expose via HTTP and provide the answer to GIT_ASKPASS. -Example of the auth url output: +Example of the servcie's output, see more at https://git-scm.com/docs/gitcredentials -``` +```json username=xxx@example.com -password=ya29.xxxxyyyyzzzz +password=ya29.mysecret ``` -# Step 2: Configure git-sync container +## Step 2: Configure git-sync container -In your git-sync container configuration, specify the auth url. +In your git-sync container configuration, specify the GIT_ASKPASS_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. +and GIT ASKPASS Service are secure. -``` +The recommended way is the ASKPASS Service running within the same pod as git-sync. + +See https://github.com/cydu-cloud/git-askpass-gce-node as a full example which use GCE Node Service Account credential to access Google Cloud Source Repo. + +```json { name: "git-sync", ... @@ -29,8 +31,8 @@ the same pod as git-sync. 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", + name: "GIT_ASKPASS_URL", + value: "http://localhost:9102/git_askpass", }, ... ] diff --git a/docs/cookie-file.md b/docs/cookie-file.md index e06408d..894cae3 100644 --- a/docs/cookie-file.md +++ b/docs/cookie-file.md @@ -2,27 +2,27 @@ Git-sync supports use of an HTTP Cookie File for accessing git content. -# Step 1: Create Secret +## Step 1: Create Secret First, create a secret file from the git cookie file you wish to use. Example: if the cookie-file is `~/.gitcookies`: -``` +```bash kubectl create secret generic git-cookie-file --from-file=cookie_file=~/.gitcookies ``` -Note that the key is `cookie_file`. This is the filename that git-sync will look +Note that the key is `cookie_file`. This is the filename that git-sync will look for. -# Step 2: Configure Pod/Deployment Volume +## Step 2: Configure Pod/Deployment Volume In your Pod or Deployment configuration, specify a Volume for mounting the cookie-file Secret. Make sure to set `secretName` to the same name you used to create the secret (`git-cookie-file` in the example above). -``` +```json volumes: [ { "name": "git-secret", @@ -34,7 +34,7 @@ volumes: [ ], ``` -# Step 3: 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 @@ -42,7 +42,7 @@ environment variable `GIT_COOKIE_FILE` to "true", and to use a git repo (`--repo` flag or `GIT_SYNC_REPO` env) is set to use a URL with the HTTP protocol. -``` +```json { name: "git-sync", ... From 6c6c354c7292f2cb62fd2d9c418b4110be42775d Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Tue, 17 Dec 2019 11:02:03 -0800 Subject: [PATCH 03/11] update docs from auth-url to askpass-url --- cmd/git-sync/main.go | 2 +- docs/{auth-url.md => askpass-url.md} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename docs/{auth-url.md => askpass-url.md} (100%) diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index bd64f4d..6765fcb 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -237,7 +237,7 @@ func main() { } if (*flUsername != "" || *flPassword != "" || *flCookieFile || *flAskPassURL != "") && *flSSH { - fmt.Fprintf(os.Stderr, "ERROR: --ssh is set but --username, --password, --auth-url, or --cookie-file were provided\n") + fmt.Fprintf(os.Stderr, "ERROR: --ssh is set but --username, --password, --askpass-url, or --cookie-file were provided\n") os.Exit(1) } diff --git a/docs/auth-url.md b/docs/askpass-url.md similarity index 100% rename from docs/auth-url.md rename to docs/askpass-url.md From 1f67515afae470f6627048e16303de169d8ef5e6 Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Tue, 17 Dec 2019 11:16:06 -0800 Subject: [PATCH 04/11] fix docs link --- docs/askpass-url.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/askpass-url.md b/docs/askpass-url.md index 9f85ae0..f226dda 100644 --- a/docs/askpass-url.md +++ b/docs/askpass-url.md @@ -4,7 +4,7 @@ The GIT ASKPASS Service expose via HTTP and provide the answer to GIT_ASKPASS. -Example of the servcie's output, see more at https://git-scm.com/docs/gitcredentials +Example of the servcie's output, see more at ```json username=xxx@example.com @@ -20,7 +20,7 @@ and GIT ASKPASS Service are secure. The recommended way is the ASKPASS Service running within the same pod as git-sync. -See https://github.com/cydu-cloud/git-askpass-gce-node as a full example which use GCE Node Service Account credential to access Google Cloud Source Repo. +See as a full example which use GCE Node Service Account credential to access Google Cloud Source Repo. ```json { From 34daaefbb8975c4b52738f4a3090e9e4fbe657e4 Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Tue, 17 Dec 2019 11:24:03 -0800 Subject: [PATCH 05/11] update some docs to retrigger the scan --- docs/ssh.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/ssh.md b/docs/ssh.md index d46f763..5208696 100644 --- a/docs/ssh.md +++ b/docs/ssh.md @@ -11,7 +11,7 @@ This can be done one of two ways: Obtain the host keys for your git server: -``` +```bash ssh-keyscan $YOUR_GIT_HOST > /tmp/known_hosts ``` @@ -19,8 +19,7 @@ Use the `kubectl create secret` command and point to the file on your filesystem that stores the key. Ensure that the file is mapped to "ssh" as shown (the file can be located anywhere). - -``` +```bash kubectl create secret generic git-creds \ --from-file=ssh=$HOME/.ssh/id_rsa \ --from-file=known_hosts=/tmp/known_hosts @@ -31,7 +30,7 @@ kubectl create secret generic git-creds \ Write a config file for a Secret that holds your SSH private key, with the key (pasted in base64 encoded plaintext) mapped to the "ssh" field. -``` +```json { "kind": "Secret", "apiVersion": "v1", @@ -47,7 +46,7 @@ Write a config file for a Secret that holds your SSH private key, with the key Create the Secret using `kubectl create -f`. -``` +```bash kubectl create -f /path/to/secret-config.json ``` @@ -57,7 +56,7 @@ In your Pod or Deployment configuration, specify a volume for mounting the Secret. Ensure that secretName matches the name you used when creating the Secret (e.g. "git-creds" used in both above examples). -``` +```yaml # ... volumes: - name: git-secret @@ -76,7 +75,7 @@ git@github.com/foo/bar) , and set the `-ssh` flags (or set GIT_SYNC_SSH to "true"). You will also need to set your container's `securityContext` to run as user ID "65533" which is created for running git-sync as non-root. -``` +```yaml # ... containers: - name: git-sync @@ -97,7 +96,7 @@ as user ID "65533" which is created for running git-sync as non-root. Lastly, you need to tell your Pod to run with the git-sync FS group. Note that this is a Pod-wide setting, unlike the container `securityContext` above. -``` +```yaml # ... securityContext: fsGroup: 65533 # to make SSH key readable @@ -113,7 +112,7 @@ restrictive enough to be used as an SSH key), so make sure you set the In case the above YAML snippets are confusing (because whitespace matters in YAML), here is a full example: -``` +```yaml apiVersion: apps/v1 kind: Deployment metadata: From c57553a2ebcfd171fd2d5bab07763979d0942dad Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Wed, 18 Dec 2019 10:52:02 -0800 Subject: [PATCH 06/11] fix docs --- docs/askpass-url.md | 34 ++++++++++++++---------------- docs/cookie-file.md | 50 ++++++++++++++++----------------------------- docs/ssh.md | 3 ++- 3 files changed, 35 insertions(+), 52 deletions(-) diff --git a/docs/askpass-url.md b/docs/askpass-url.md index f226dda..841cc47 100644 --- a/docs/askpass-url.md +++ b/docs/askpass-url.md @@ -2,9 +2,9 @@ ## Step 1: Create a GIT_ASKPASS HTTP Service -The GIT ASKPASS Service expose via HTTP and provide the answer to GIT_ASKPASS. +The GIT ASKPASS Service is exposed via HTTP and provide the answer to GIT_ASKPASS. -Example of the servcie's output, see more at +Example of the service's output, see more at ```json username=xxx@example.com @@ -18,23 +18,19 @@ In your git-sync container configuration, specify the GIT_ASKPASS_URL The credentials will pass in plain text, make sure the connection between git-sync and GIT ASKPASS Service are secure. -The recommended way is the ASKPASS Service running within the same pod as git-sync. +The recommended situation are: -See as a full example which use GCE Node Service Account credential to access Google Cloud Source Repo. +* ASKPASS Service running within the same pod as git-sync. +* ASKPASS Service rely on [GCE metadata](https://cloud.google.com/compute/docs/storing-retrieving-metadata) to get service account's credential to access Google Cloud Source Repo. -```json -{ - name: "git-sync", - ... - env: [ - { - name: "GIT_SYNC_REPO", - value: "https://source.developers.google.com/p/[GCP PROJECT ID]/r/[REPO NAME]" - }, { - name: "GIT_ASKPASS_URL", - value: "http://localhost:9102/git_askpass", - }, - ... - ] -} +See as a full example. + +```yaml +name: "git-sync" +... +env: + - name: "GIT_SYNC_REPO", + value: "https://source.developers.google.com/p/[GCP PROJECT ID]/r/[REPO NAME]" + - name: "GIT_ASKPASS_URL", + value: "http://localhost:9102/git_askpass", ``` diff --git a/docs/cookie-file.md b/docs/cookie-file.md index 894cae3..8bbaeb2 100644 --- a/docs/cookie-file.md +++ b/docs/cookie-file.md @@ -22,16 +22,12 @@ In your Pod or Deployment configuration, specify a Volume for mounting the cookie-file Secret. Make sure to set `secretName` to the same name you used to create the secret (`git-cookie-file` in the example above). -```json -volumes: [ - { - "name": "git-secret", - "secret": { - "secretName": "git-cookie-file", - } - }, - ... -], +```yaml +volumes: + - name: git-secret + secret: + secretName: git-cookie-file + defaultMode: 0440 ``` ## Step 3: Configure git-sync container @@ -42,26 +38,16 @@ environment variable `GIT_COOKIE_FILE` to "true", and to use a git repo (`--repo` flag or `GIT_SYNC_REPO` env) is set to use a URL with the HTTP protocol. -```json -{ - name: "git-sync", - ... - env: [ - { - name: "GIT_SYNC_REPO", - value: "https://github.com/kubernetes/kubernetes.git" - }, { - name: "GIT_COOKIE_FILE", - value: "true", - }, - ... - ] - volumeMounts: [ - { - "name": "git-secret", - "mountPath": "/etc/git-secret" - }, - ... - ], -} +```yaml +name: "git-sync" +... +env: + - name: GIT_SYNC_REPO + value: https://github.com/kubernetes/kubernetes.git + - name: GIT_COOKIE_FILE + value: true +volumeMounts: + - name: git-secret + mountPath: /etc/git-secret + readOnly: true ``` diff --git a/docs/ssh.md b/docs/ssh.md index 5208696..8f7519a 100644 --- a/docs/ssh.md +++ b/docs/ssh.md @@ -130,7 +130,7 @@ spec: - name: git-secret secret: secretName: git-creds - defaultMode: 288 # = mode 0440 + defaultMode: 0440 containers: - name: git-sync image: k8s.gcr.io/git-sync:v3.1.1 @@ -145,6 +145,7 @@ spec: volumeMounts: - name: git-secret mountPath: /etc/git-secret + readOnly: true securityContext: fsGroup: 65533 # to make SSH key readable ``` From 31f276dd5814f379d3e2b837887531632e595dae Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Wed, 18 Dec 2019 10:59:20 -0800 Subject: [PATCH 07/11] fix comments --- cmd/git-sync/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index 6765fcb..3313bfd 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -583,7 +583,7 @@ func revIsHash(ctx context.Context, rev, gitRoot string) (bool, error) { // 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, authUrl string) (bool, string, error) { if authUrl != "" { - // For Auth Callback URL, the credentials behind is dynamic, it needs to be + // For ASKPASS Callback URL, the credentials behind is dynamic, it needs to be // re-fetched each time. if err := setupGitAskPassURL(ctx); err != nil { return false, "", fmt.Errorf("failed to call GIT_ASKPASS_URL: %v", err) From b0bdc02e8bec14a292c065ff3f95f9021320dcff Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Wed, 18 Dec 2019 20:05:51 -0800 Subject: [PATCH 08/11] manually merge https://github.com/kubernetes/git-sync/pull/217 --- askpass_git.sh | 27 +++++++++++++++++++++++++ cmd/git-sync/main.go | 4 ++-- test_e2e.sh | 47 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100755 askpass_git.sh diff --git a/askpass_git.sh b/askpass_git.sh new file mode 100755 index 0000000..5a33a7c --- /dev/null +++ b/askpass_git.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Ask pass when cloning new repo, fail if it mismatched the magic password. + +mkdir -p "${XDG_CONFIG_HOME}/git/" +# Override the default 'git --global' config location, the default location +# outside the e2e test environment. See https://git-scm.com/docs/git-config +touch "${XDG_CONFIG_HOME}/git/config" +# Override the default 'git credential store' config location, the default location +# outside the e2e test environment. See https://git-scm.com/docs/git-credential-store +touch "${XDG_CONFIG_HOME}/git/credentials" + +if [ "$1" != "clone" ]; then + git "$@" + exit $? +fi + +# `git credential fill` requires the repo url match to consume the credentials stored by git-sync. +# Askpass git only support repo started with "file://" which is used in test_e2e.sh. +REPO=$(echo "$@" | grep -o "file://[^ ]*") +PASSWD=$(echo "url=${REPO}" | git credential fill | grep -o "password=.*") +# Test case much match the magic password below. +if [ "${PASSWD}" != "password=Lov3!k0os" ]; then + echo "invalid password ${PASSWD}, try Lov3!k0os next time." + exit 1 +fi + +git "$@" diff --git a/cmd/git-sync/main.go b/cmd/git-sync/main.go index 3313bfd..e8ec321 100644 --- a/cmd/git-sync/main.go +++ b/cmd/git-sync/main.go @@ -711,9 +711,9 @@ func runCommandWithStdin(ctx context.Context, cwd, stdin, command string, args . } func setupGitAuth(ctx context.Context, username, password, gitURL string) error { - log.V(1).Info("setting up git credential cache") + log.V(1).Info("setting up git credential store") - _, err := runCommand(ctx, "", *flGitCmd, "config", "--global", "credential.helper", "cache") + _, err := runCommand(ctx, "", *flGitCmd, "config", "--global", "credential.helper", "store") if err != nil { return fmt.Errorf("error setting up git credentials: %v", err) } diff --git a/test_e2e.sh b/test_e2e.sh index c680362..d3ae055 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -89,6 +89,8 @@ function GIT_SYNC() { -u $(id -u):$(id -g) \ -v "$DIR":"$DIR" \ -v "$(pwd)/slow_git.sh":"/slow_git.sh" \ + -v "$(pwd)/askpass_git.sh":"/askpass_git.sh" \ + --env XDG_CONFIG_HOME=$DIR \ --network="host" \ --rm \ e2e/git-sync:$(make -s version)__$(go env GOOS)_$(go env GOARCH) \ @@ -102,6 +104,7 @@ function remove_sync_container() { } SLOW_GIT=/slow_git.sh +ASKPASS_GIT=/askpass_git.sh REPO="$DIR/repo" mkdir "$REPO" @@ -621,6 +624,48 @@ remove_sync_container wait pass +############################################## +# Test password +############################################## +testcase "password" +echo "$TESTCASE 1" > "$REPO"/file +git -C "$REPO" commit -qam "$TESTCASE 1" +# run with askpass_git but with wrong password +GIT_SYNC \ + --git=$ASKPASS_GIT \ + --username="you@example.com" \ + --password="I have no idea what the password is." \ + --logtostderr \ + --v=5 \ + --one-time \ + --repo="file://$REPO" \ + --branch=master \ + --rev=HEAD \ + --root="$ROOT" \ + --dest="link" \ + > "$DIR"/log."$TESTCASE" 2>&1 || true +# check for failure +assert_file_absent "$ROOT"/link/file +# run with askpass_git with correct password +GIT_SYNC \ + --git=$ASKPASS_GIT \ + --username="you@example.com" \ + --password="Lov3!k0os" \ + --logtostderr \ + --v=5 \ + --one-time \ + --repo="file://$REPO" \ + --branch=master \ + --rev=HEAD \ + --root="$ROOT" \ + --dest="link" \ + > "$DIR"/log."$TESTCASE" 2>&1 +assert_link_exists "$ROOT"/link +assert_file_exists "$ROOT"/link/file +assert_file_eq "$ROOT"/link/file "$TESTCASE 1" +# Wrap up +pass + ############################################## # Test webhook ############################################## @@ -788,7 +833,7 @@ assert_file_absent "$ROOT"/link/$SUBMODULE_REPO_NAME/$NESTED_SUBMODULE_REPO_NAME # Remove submodule git -C "$REPO" submodule deinit -q $SUBMODULE_REPO_NAME rm -rf "$REPO"/.git/modules/$SUBMODULE_REPO_NAME -git -C "$REPO" rm -qf $SUBMODULE_REPO_NAME +git -C "$REPO" rm -qf $SUBMODULE_REPO_NAME git -C "$REPO" commit -aqm "delete submodule" sleep 3 assert_link_exists "$ROOT"/link From d8d9ff72b8bc705ecb99cbb38271bbcf805eec45 Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Wed, 18 Dec 2019 21:07:01 -0800 Subject: [PATCH 09/11] add e2e test for askpasswd_url --- docs/askpass-url.md | 9 ++------- test_e2e.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/docs/askpass-url.md b/docs/askpass-url.md index 841cc47..e75f6a4 100644 --- a/docs/askpass-url.md +++ b/docs/askpass-url.md @@ -6,7 +6,7 @@ The GIT ASKPASS Service is exposed via HTTP and provide the answer to GIT_ASKPAS Example of the service's output, see more at -```json +``` username=xxx@example.com password=ya29.mysecret ``` @@ -18,12 +18,7 @@ In your git-sync container configuration, specify the GIT_ASKPASS_URL The credentials will pass in plain text, make sure the connection between git-sync and GIT ASKPASS Service are secure. -The recommended situation are: - -* ASKPASS Service running within the same pod as git-sync. -* ASKPASS Service rely on [GCE metadata](https://cloud.google.com/compute/docs/storing-retrieving-metadata) to get service account's credential to access Google Cloud Source Repo. - -See as a full example. +See askpass_url e2e test as an example. ```yaml name: "git-sync" diff --git a/test_e2e.sh b/test_e2e.sh index d3ae055..3c90db9 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -666,6 +666,51 @@ assert_file_eq "$ROOT"/link/file "$TESTCASE 1" # Wrap up pass +############################################## +# Test askpass_url +############################################## +testcase "askpass_url" +echo "$TESTCASE 1" > "$REPO"/file +NCPORT=8888 +git -C "$REPO" commit -qam "$TESTCASE 1" +# run the askpass_url service with wrong password +# Need to run it twice one for setup another for real clone +{ (for i in 1 2; do echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=you@example.com\npassword=dummypw' | nc -N -l $NCPORT > /dev/null; done) &} +GIT_SYNC \ + --git=$ASKPASS_GIT \ + --askpass-url="http://localhost:$NCPORT/git_askpass" \ + --logtostderr \ + --v=5 \ + --one-time \ + --repo="file://$REPO" \ + --branch=master \ + --rev=HEAD \ + --root="$ROOT" \ + --dest="link" \ + > "$DIR"/log."$TESTCASE" 2>&1 || true +# check for failure +assert_file_absent "$ROOT"/link/file +# run with askpass_url service with correct password +# Need to run it twice one for setup another for real clone +{ (for i in 1 2; do echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=you@example.com\npassword=Lov3!k0os' | nc -N -l $NCPORT > /dev/null; done) &} +GIT_SYNC \ + --git=$ASKPASS_GIT \ + --askpass-url="http://localhost:$NCPORT/git_askpass" \ + --logtostderr \ + --v=5 \ + --one-time \ + --repo="file://$REPO" \ + --branch=master \ + --rev=HEAD \ + --root="$ROOT" \ + --dest="link" \ + > "$DIR"/log."$TESTCASE" 2>&1 +assert_link_exists "$ROOT"/link +assert_file_exists "$ROOT"/link/file +assert_file_eq "$ROOT"/link/file "$TESTCASE 1" +# Wrap up +pass + ############################################## # Test webhook ############################################## From 0b0f0a62d5edc70fc8af26a0f70b418f20853f4e Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Wed, 18 Dec 2019 22:30:42 -0800 Subject: [PATCH 10/11] use random port for nc --- test_e2e.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test_e2e.sh b/test_e2e.sh index 8e05d9d..465322c 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -671,7 +671,7 @@ pass ############################################## testcase "askpass_url" echo "$TESTCASE 1" > "$REPO"/file -NCPORT=8888 +NCPORT=$(($RANDOM+2000)) git -C "$REPO" commit -qam "$TESTCASE 1" # run the askpass_url service with wrong password # Need to run it twice one for setup another for real clone @@ -715,7 +715,7 @@ pass # Test webhook ############################################## testcase "webhook" -NCPORT=8888 +NCPORT=$(($RANDOM+2000)) # First sync echo "$TESTCASE 1" > "$REPO"/file git -C "$REPO" commit -qam "$TESTCASE 1" @@ -972,4 +972,4 @@ wait pass echo "cleaning up $DIR" -rm -rf "$DIR" \ No newline at end of file +rm -rf "$DIR" From 0851cc59165c2133ec86bcd0bfbaa5a0cc916d80 Mon Sep 17 00:00:00 2001 From: Chuanying Du Date: Thu, 19 Dec 2019 16:26:46 -0800 Subject: [PATCH 11/11] use free port --- test_e2e.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test_e2e.sh b/test_e2e.sh index 465322c..c39e4c9 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -52,6 +52,14 @@ function assert_file_eq() { fail "file $1 does not contain '$2': $(cat $1)" } +NCPORT=8888 +function freencport() { + while :; do + NCPORT=$((RANDOM+2000)) + ss -lpn | grep -q ":$NCPORT " || break + done +} + # ##################### # main # ##################### @@ -671,10 +679,9 @@ pass ############################################## testcase "askpass_url" echo "$TESTCASE 1" > "$REPO"/file -NCPORT=$(($RANDOM+2000)) +freencport git -C "$REPO" commit -qam "$TESTCASE 1" # run the askpass_url service with wrong password -# Need to run it twice one for setup another for real clone { (for i in 1 2; do echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=you@example.com\npassword=dummypw' | nc -N -l $NCPORT > /dev/null; done) &} GIT_SYNC \ --git=$ASKPASS_GIT \ @@ -691,7 +698,6 @@ GIT_SYNC \ # check for failure assert_file_absent "$ROOT"/link/file # run with askpass_url service with correct password -# Need to run it twice one for setup another for real clone { (for i in 1 2; do echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=you@example.com\npassword=Lov3!k0os' | nc -N -l $NCPORT > /dev/null; done) &} GIT_SYNC \ --git=$ASKPASS_GIT \ @@ -715,7 +721,7 @@ pass # Test webhook ############################################## testcase "webhook" -NCPORT=$(($RANDOM+2000)) +freencport # First sync echo "$TESTCASE 1" > "$REPO"/file git -C "$REPO" commit -qam "$TESTCASE 1"