Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add github app auth to git artifactdriver #13823

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,20 @@ require (
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect
github.com/bradleyfalzon/ghinstallation v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/evilmonkeyinc/jsonpath v0.8.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-github/v29 v29.0.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/blushft/go-diagrams v0.0.0-20201006005127-c78c821223d9 h1:mV+hh0rMjzrhg7Jc/GKwpa+y/0BMHGOHdM9yY1GYyFI=
github.com/blushft/go-diagrams v0.0.0-20201006005127-c78c821223d9/go.mod h1:nDeXEIaeDV+mAK1gBD3/RJH67DYPC0GdaznWN7sB07s=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bradleyfalzon/ghinstallation v1.1.1 h1:pmBXkxgM1WeF8QYvDLT5kuQiHMcmf+X015GI0KM/E3I=
github.com/bradleyfalzon/ghinstallation v1.1.1/go.mod h1:vyCmHTciHx/uuyN82Zc3rXN3X2KTK8nUTCrTMwAhcug=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
Expand Down Expand Up @@ -210,6 +212,7 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE=
github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
Expand Down Expand Up @@ -391,6 +394,11 @@ github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220720195016-
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220720195016-31786c6cbb82/go.mod h1:oiF0XG4IIPHc9kusCLuBDwY0ov4e1cikrP/vAqSp868=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220719135131-f79ec2192282 h1:Mjy6sd3HMC/PU+p4zuy3R5p5oevK8PggZms07Mgr95I=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20220719135131-f79ec2192282/go.mod h1:gbpYHfGj1oNhLX9gla4EjsnlJ44Ng+/kJRKi09FiXOU=
github.com/google/go-github/v29 v29.0.2 h1:opYN6Wc7DOz7Ku3Oh4l7prmkOMwEcQxpFtxdU8N8Pts=
github.com/google/go-github/v29 v29.0.2/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-github/v29 v29.0.3 h1:IktKCTwU//aFHnpA+2SLIi7Oo9uhAzgsdZNbcAqhgdc=
github.com/google/go-github/v29 v29.0.3/go.mod h1:CHKiKKPHJ0REzfwc14QMklvtHwCveD0PxlMjLlzAM5E=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/api-rules/violation_exceptions.list
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ API rule violation: list_type_missing,github.com/argoproj/argo-workflows/v3/pkg/
API rule violation: list_type_missing,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,WorkflowStep,WithItems
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,ArtifactSearchResult,Artifact
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,ArtifactSearchResult,NodeID
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,GitArtifact,GithubAppAuth
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,HTTPAuth,OAuth2
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,SubmitOpts,Entrypoint
API rule violation: names_match,github.com/argoproj/argo-workflows/v3/pkg/apis/workflow/v1alpha1,WorkflowStatus,StoredWorkflowSpec
18 changes: 18 additions & 0 deletions pkg/apis/workflow/v1alpha1/workflow_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2619,6 +2619,24 @@ type GitArtifact struct {

// InsecureSkipTLS disables server certificate verification resulting in insecure HTTPS connections
InsecureSkipTLS bool `json:"insecureSkipTLS,omitempty" protobuf:"varint,12,opt,name=insecureSkipTLS"`

// GithubApp is the GitHub App authentication method
GithubAppAuth *GithubAppAuth `json:"githubApp,omitempty" protobuf:"bytes,13,opt,name=githubApp"`
}

type GithubAppAuth struct {

// InstallationID is the GitHub App installation ID
InstallationID int64 `json:"installationID,omitempty" protobuf:"varint,1,opt,name=installationID"`

// AppID is the GitHub App ID
AppID int64 `json:"appID,omitempty" protobuf:"varint,2,opt,name=appID"`

// PrivateKeySecret is the secret selector to the GitHub App private key
PrivateKeySecret *apiv1.SecretKeySelector `json:"privateKeySecret,omitempty" protobuf:"bytes,3,opt,name=privateKeySecret"`

// BaseURL is the GitHub API base URL
BaseURL string `json:"baseURL,omitempty" protobuf:"bytes,4,opt,name=baseURL"`
}

func (g *GitArtifact) HasLocation() bool {
Expand Down
13 changes: 13 additions & 0 deletions workflow/artifacts/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ func newDriver(ctx context.Context, art *wfv1.Artifact, ri resource.Interface) (
}
gitDriver.SSHPrivateKey = sshPrivateKeyBytes
}
if art.Git.GithubAppAuth != nil {
privateKeyBytes, err := ri.GetSecret(ctx, art.Git.GithubAppAuth.PrivateKeySecret.Name, art.Git.GithubAppAuth.PrivateKeySecret.Key)
if err != nil {
return nil, err
}
gitDriver.GithubApp = &git.GithubApp{
InstallationID: art.Git.GithubAppAuth.InstallationID,
PrivateKey: privateKeyBytes,
ID: art.Git.GithubAppAuth.AppID,
BaseURL: art.Git.GithubAppAuth.BaseURL,
}

}

return &gitDriver, nil
}
Expand Down
45 changes: 45 additions & 0 deletions workflow/artifacts/git/git.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package git

import (
"context"
"errors"
"fmt"
"io"
nethttp "net/http"
"os"
"regexp"

"github.com/bradleyfalzon/ghinstallation"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
ssh2 "github.com/go-git/go-git/v5/plumbing/transport/ssh"

"github.com/google/go-github/v29/github"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"

Expand All @@ -21,6 +26,10 @@ import (
"github.com/argoproj/argo-workflows/v3/workflow/artifacts/common"
)

const (
DefaultGithubUrl = "https://api.github.com"
)

// ArtifactDriver is the artifact driver for a git repo
type ArtifactDriver struct {
Username string
Expand All @@ -29,6 +38,14 @@ type ArtifactDriver struct {
InsecureIgnoreHostKey bool
InsecureSkipTLS bool
DisableSubmodules bool
GithubApp *GithubApp
}

type GithubApp struct {
InstallationID int64
PrivateKey string
ID int64
BaseURL string
}

var _ common.ArtifactDriver = &ArtifactDriver{}
Expand Down Expand Up @@ -67,6 +84,34 @@ func (g *ArtifactDriver) auth(sshUser string) (func(), transport.AuthMethod, err
}
return func() { _ = os.Remove(privateKeyFile.Name()) }, auth, nil
}

if g.GithubApp != nil {
privateKey := []byte(g.GithubApp.PrivateKey)
transport, err := ghinstallation.New(nethttp.DefaultTransport, g.GithubApp.ID, g.GithubApp.InstallationID, privateKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to create transport: %w", err)
}

if g.GithubApp.BaseURL != "" {
transport.BaseURL = g.GithubApp.BaseURL
} else {
transport.BaseURL = DefaultGithubUrl
}

var client *github.Client

httpClient := nethttp.Client{Transport: transport}
client = github.NewClient(&httpClient)

token, _, err := client.Apps.CreateInstallationToken(context.Background(), g.GithubApp.InstallationID, nil)
if err != nil {
return nil, nil, fmt.Errorf("failed to create installation token: %w", err)
}
return func() {}, &http.BasicAuth{
Username: "x-access-token",
Password: token.GetToken(),
}, nil
}
if g.Username != "" || g.Password != "" {
return func() {}, &http.BasicAuth{Username: g.Username, Password: g.Password}, nil
}
Expand Down
7 changes: 7 additions & 0 deletions workflow/artifacts/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ func TestGitArtifactDriver_Load(t *testing.T) {
require.NoError(t, load(driver, &wfv1.GitArtifact{Repo: "https://github.com/argoproj-labs/private-test-repo.git"}))
assert.FileExists(t, path+"/README.md")
})
t.Run("GithubApp", func(t *testing.T) {
privateKey, err := os.ReadFile(homedir.HomeDir() + "/.ssh/id_rsa")
require.NoError(t, err)

driver := &ArtifactDriver{GithubApp: &GithubApp{InstallationID: 123, PrivateKey: string(privateKey), ID: 456, BaseURL: "https://api.github.com"}}
require.NoError(t, load(driver, &wfv1.GitArtifact{Repo: "https://github.com/argoproj-labs/private-test-repo.git"}))
})
})
t.Run("PublicRepo", func(t *testing.T) {
driver := &ArtifactDriver{}
Expand Down
Loading