From 4ec9a700bfbef258ccde5079caf8a85fdd80f841 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Thu, 3 Oct 2024 10:26:51 -0700 Subject: [PATCH] Adds support for setting customg it auth By default git auth supports some default secret values for fetching auth credentials (`GIT_AUTH_TOKEN` and `GIT_AUTH_HEADER`). This change allows customizing that to different secret names. Signed-off-by: Brian Goff --- docs/spec.schema.json | 21 ++++++++++++++++ helpers.go | 6 +++++ source.go | 1 + source_test.go | 56 +++++++++++++++++++++++++++++++++++++++++ spec.go | 48 ++++++++++++++++++++++++++++++++--- website/docs/sources.md | 23 +++++++++++++++-- 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/docs/spec.schema.json b/docs/spec.schema.json index 6289ea4fd..4fa9bb53b 100644 --- a/docs/spec.schema.json +++ b/docs/spec.schema.json @@ -393,6 +393,24 @@ "type": "object", "description": "GeneratorGomod is used to generate a go module cache from go module sources" }, + "GitAuth": { + "properties": { + "header": { + "type": "string", + "description": "Header is the name of the secret which contains the git auth header.\nwhen using git auth header based authentication.\nNote: This should not have the *actual* secret value, just the name of\nthe secret which was specified as a build secret." + }, + "token": { + "type": "string", + "description": "Token is the name of the secret which contains a git auth token when using\ntoken based authentication.\nNote: This should not have the *actual* secret value, just the name of\nthe secret which was specified as a build secret." + }, + "ssh": { + "type": "string", + "description": "SSH is the name of the secret which contains the ssh auth into when using\nssh based auth.\nNote: This should not have the *actual* secret value, just the name of\nthe secret which was specified as a build secret." + } + }, + "additionalProperties": false, + "type": "object" + }, "ImageConfig": { "properties": { "entrypoint": { @@ -716,6 +734,9 @@ }, "keepGitDir": { "type": "boolean" + }, + "auth": { + "$ref": "#/$defs/GitAuth" } }, "additionalProperties": false, diff --git a/helpers.go b/helpers.go index 0d4832e37..43f8b4351 100644 --- a/helpers.go +++ b/helpers.go @@ -413,3 +413,9 @@ func (s *Spec) GetPackageDeps(target string) *PackageDependencies { } return s.Dependencies } + +type gitOptionFunc func(*llb.GitInfo) + +func (f gitOptionFunc) SetGitOption(gi *llb.GitInfo) { + f(gi) +} diff --git a/source.go b/source.go index 55900ff55..770f1e739 100644 --- a/source.go +++ b/source.go @@ -137,6 +137,7 @@ func (src *SourceGit) AsState(opts ...llb.ConstraintsOpt) (llb.State, error) { gOpts = append(gOpts, llb.KeepGitDir()) } gOpts = append(gOpts, withConstraints(opts)) + gOpts = append(gOpts, src.Auth.LLBOpt()) st := llb.Git(ref.Remote, src.Commit, gOpts...) return st, nil diff --git a/source_test.go b/source_test.go index cf55700e2..d7fd21142 100644 --- a/source_test.go +++ b/source_test.go @@ -87,6 +87,19 @@ func TestSourceGitSSH(t *testing.T) { checkFilter(t, ops2[1].GetFile(), &src) }) + + t.Run("auth", func(t *testing.T) { + src := Source{ + Git: &SourceGit{ + URL: fmt.Sprintf("user@%s:test.git", addr), + Commit: t.Name(), + }, + } + + ops := getSourceOp(ctx, t, src) + checkGitOp(t, ops, &src) + }) + } func TestSourceGitHTTP(t *testing.T) { @@ -148,6 +161,22 @@ func TestSourceGitHTTP(t *testing.T) { checkFilter(t, ops2[1].GetFile(), &src) }) + + t.Run("auth", func(t *testing.T) { + src := Source{ + Git: &SourceGit{ + URL: "https://localhost/test.git", + Commit: t.Name(), + Auth: GitAuth{ + Header: "some header", + Token: "some token", + }, + }, + } + + ops := getSourceOp(ctx, t, src) + checkGitOp(t, ops, &src) + }) } func TestSourceHTTP(t *testing.T) { @@ -876,6 +905,33 @@ func checkGitOp(t *testing.T, ops []*pb.Op, src *Source) { if op.Attrs["git.fullurl"] != src.Git.URL { t.Errorf("expected git.fullurl %q, got %q", src.Git.URL, op.Attrs["git.fullurl"]) } + + const ( + defaultAuthHeader = "GIT_AUTH_HEADER" + defaultAuthToken = "GIT_AUTH_TOKEN" + defaultAuthSSH = "default" + ) + + hdr := defaultAuthHeader + if src.Git.Auth.Header != "" { + hdr = src.Git.Auth.Header + } + assert.Check(t, cmp.Equal(op.Attrs["git.authheadersecret"], hdr), op.Attrs) + + token := defaultAuthToken + if src.Git.Auth.Token != "" { + token = src.Git.Auth.Token + } + assert.Check(t, cmp.Equal(op.Attrs["git.authtokensecret"], token), op.Attrs) + + if !strings.HasPrefix(src.Git.URL, "http") { + // ssh settings are only set when using ssh based auth + ssh := defaultAuthSSH + if src.Git.Auth.SSH != "" { + ssh = src.Git.Auth.SSH + } + assert.Check(t, cmp.Equal(op.Attrs["git.mountsshsock"], ssh), op) + } } func checkFilter(t *testing.T, op *pb.FileOp, src *Source) { diff --git a/spec.go b/spec.go index 76c737b4d..7d8e9f4cd 100644 --- a/spec.go +++ b/spec.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/moby/buildkit/client/llb" "github.com/opencontainers/go-digest" ) @@ -141,9 +142,50 @@ type SourceDockerImage struct { } type SourceGit struct { - URL string `yaml:"url" json:"url"` - Commit string `yaml:"commit" json:"commit"` - KeepGitDir bool `yaml:"keepGitDir" json:"keepGitDir"` + URL string `yaml:"url" json:"url"` + Commit string `yaml:"commit" json:"commit"` + KeepGitDir bool `yaml:"keepGitDir" json:"keepGitDir"` + Auth GitAuth `yaml:"auth,omitempty" json:"auth,omitempty"` +} + +type GitAuth struct { + // Header is the name of the secret which contains the git auth header. + // when using git auth header based authentication. + // Note: This should not have the *actual* secret value, just the name of + // the secret which was specified as a build secret. + Header string `yaml:"header,omitempty" json:"header,omitempty"` + // Token is the name of the secret which contains a git auth token when using + // token based authentication. + // Note: This should not have the *actual* secret value, just the name of + // the secret which was specified as a build secret. + Token string `yaml:"token,omitempty" json:"token,omitempty"` + // SSH is the name of the secret which contains the ssh auth into when using + // ssh based auth. + // Note: This should not have the *actual* secret value, just the name of + // the secret which was specified as a build secret. + SSH string `yaml:"ssh,omitempty" json:"ssh,omitempty"` +} + +// LLBOpt returns an [llb.GitOption] which sets the auth header and token secret +// values in LLB if they are set. +func (a *GitAuth) LLBOpt() llb.GitOption { + return gitOptionFunc(func(gi *llb.GitInfo) { + if a == nil { + return + } + + if a.Header != "" { + gi.AuthHeaderSecret = a.Header + } + + if a.Token != "" { + gi.AuthTokenSecret = a.Token + } + + if a.SSH != "" { + gi.MountSSHSock = a.SSH + } + }) } // SourceHTTP is used to download a file from an HTTP(s) URL. diff --git a/website/docs/sources.md b/website/docs/sources.md index 0ef14ac5f..59101da4c 100644 --- a/website/docs/sources.md +++ b/website/docs/sources.md @@ -70,12 +70,31 @@ You can override this behavior by setting `keepGitDir: true` in the git configur Git repositories are considered to be "directory" sources. -Authentication will be handled using some defaults: +Authentication will be handled using some default secret names which are fetched +from the client: -1. Local SSH agent +1. Default SSH agent 2. Providing a build secret called `GIT_AUTH_HEADER` for header based auth 3. Providing a build secret called `GIT_AUTH_TOKEN` for token based auth +You can customize each of these by setting the appropriate field in the +git auth section (shown below with default values): + +```yaml + someSource1: + git: + # This uses an SSH style git URL. + url: git@github.com:myOrg/myRepo.git + commit: 1234567890abcdef + auth: + header: GIT_AUTH_HEADER # Default header secret used + token: GIT_AUTH_TOKEN # Default token secret used + ssh: default # Default SSH secret used +``` + +Note: These are secret names which are used to reference the secrets provided +by the client, not the actual secret values. + ### HTTP HTTP sources fetch a file from an HTTP URL.