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(appset): Add support for self-signed TLS / Certificates for Gitlab Scm Provider #14348

Merged
merged 9 commits into from
Jul 11, 2023
1 change: 1 addition & 0 deletions applicationset/controllers/applicationset_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type ApplicationSetReconciler struct {
ArgoCDNamespace string
ApplicationSetNamespaces []string
EnableProgressiveSyncs bool
SCMRootCAPath string
}

// +kubebuilder:rbac:groups=argoproj.io,resources=applicationsets,verbs=get;list;watch;create;update;patch;delete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/event"

"github.com/argoproj/argo-cd/v2/applicationset/generators"
"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/argoproj/gitops-engine/pkg/health"
"github.com/argoproj/gitops-engine/pkg/sync/common"

"github.com/argoproj/argo-cd/v2/applicationset/generators"
"github.com/argoproj/argo-cd/v2/applicationset/utils"

"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
"github.com/argoproj/argo-cd/v2/util/collections"
Expand Down
11 changes: 6 additions & 5 deletions applicationset/controllers/requeue_after_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import (
"testing"
"time"

"github.com/argoproj/argo-cd/v2/applicationset/generators"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
Expand All @@ -17,6 +14,10 @@ import (
kubefake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

"github.com/argoproj/argo-cd/v2/applicationset/generators"
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)

func TestRequeueAfter(t *testing.T) {
Expand Down Expand Up @@ -59,9 +60,9 @@ func TestRequeueAfter(t *testing.T) {
"List": generators.NewListGenerator(),
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
"Git": generators.NewGitGenerator(mockServer),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}),
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, ""),
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}),
"PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, ""),
}

nestedGenerators := map[string]generators.Generator{
Expand Down
3 changes: 1 addition & 2 deletions applicationset/generators/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli
return &appSetGenerator.Clusters.Template
}

func (g *ClusterGenerator) GenerateParams(
appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) {
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet) ([]map[string]interface{}, error) {

if appSetGenerator == nil {
return nil, EmptyAppSetGeneratorError
Expand Down
3 changes: 1 addition & 2 deletions applicationset/generators/duck_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package generators
import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
Expand All @@ -15,8 +16,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"

argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"

"testing"
)

const resourceApiVersion = "mallard.io/v1"
Expand Down
3 changes: 2 additions & 1 deletion applicationset/generators/generator_spec_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"fmt"
"reflect"

"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/jeremywohl/flatten"

"github.com/argoproj/argo-cd/v2/applicationset/utils"

"k8s.io/apimachinery/pkg/labels"

argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"context"
"testing"

"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"

argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"

"github.com/stretchr/testify/mock"
Expand Down
3 changes: 2 additions & 1 deletion applicationset/generators/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"fmt"
"time"

argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"sigs.k8s.io/yaml"

argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)

var _ Generator = (*ListGenerator)(nil)
Expand Down
10 changes: 6 additions & 4 deletions applicationset/generators/pull_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ type PullRequestGenerator struct {
client client.Client
selectServiceProviderFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
auth SCMAuthProviders
scmRootCAPath string
}

func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders) Generator {
func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string) Generator {
g := &PullRequestGenerator{
client: client,
auth: auth,
client: client,
auth: auth,
scmRootCAPath: scmRootCAPath,
}
g.selectServiceProviderFunc = g.selectServiceProvider
return g
Expand Down Expand Up @@ -126,7 +128,7 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
}
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState)
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure)
}
if generatorConfig.Gitea != nil {
providerConfig := generatorConfig.Gitea
Expand Down
6 changes: 4 additions & 2 deletions applicationset/generators/scm_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,18 @@ type SCMProviderGenerator struct {
// Testing hooks.
overrideProvider scm_provider.SCMProviderService
SCMAuthProviders
scmRootCAPath string
}

type SCMAuthProviders struct {
GitHubApps github_app_auth.Credentials
}

func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders) Generator {
func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string) Generator {
return &SCMProviderGenerator{
client: client,
SCMAuthProviders: providers,
scmRootCAPath: scmRootCAPath,
}
}

Expand Down Expand Up @@ -85,7 +87,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
if err != nil {
return nil, fmt.Errorf("error fetching Gitlab token: %v", err)
}
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups)
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Gitlab.Group, token, providerConfig.Gitlab.API, providerConfig.Gitlab.AllBranches, providerConfig.Gitlab.IncludeSubgroups, providerConfig.Gitlab.Insecure, g.scmRootCAPath)
if err != nil {
return nil, fmt.Errorf("error initializing Gitlab service: %v", err)
}
Expand Down
13 changes: 12 additions & 1 deletion applicationset/services/pull_request/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package pull_request
import (
"context"
"fmt"
"net/http"
"os"

"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/hashicorp/go-retryablehttp"
gitlab "github.com/xanzy/go-gitlab"
)

Expand All @@ -17,7 +20,7 @@ type GitLabService struct {

var _ PullRequestService = (*GitLabService)(nil)

func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string) (PullRequestService, error) {
func NewGitLabService(ctx context.Context, token, url, project string, labels []string, pullRequestState string, scmRootCAPath string, insecure bool) (PullRequestService, error) {
var clientOptionFns []gitlab.ClientOptionFunc

// Set a custom Gitlab base URL if one is provided
Expand All @@ -29,6 +32,14 @@ func NewGitLabService(ctx context.Context, token, url, project string, labels []
token = os.Getenv("GITLAB_TOKEN")
}

tr := &http.Transport{
TLSClientConfig: utils.GetTlsConfig(scmRootCAPath, insecure),
}
retryClient := retryablehttp.NewClient()
retryClient.HTTPClient.Transport = tr

clientOptionFns = append(clientOptionFns, gitlab.WithHTTPClient(retryClient.HTTPClient))

client, err := gitlab.NewClient(token, clientOptionFns...)
if err != nil {
return nil, fmt.Errorf("error creating Gitlab client: %v", err)
Expand Down
10 changes: 5 additions & 5 deletions applicationset/services/pull_request/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestGitLabServiceCustomBaseURL(t *testing.T) {
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "")
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil, "", "", false)
assert.NoError(t, err)

_, err = svc.List(context.Background())
Expand All @@ -53,7 +53,7 @@ func TestGitLabServiceToken(t *testing.T) {
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "")
svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil, "", "", false)
assert.NoError(t, err)

_, err = svc.List(context.Background())
Expand All @@ -72,7 +72,7 @@ func TestList(t *testing.T) {
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "")
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "", "", false)
assert.NoError(t, err)

prs, err := svc.List(context.Background())
Expand All @@ -96,7 +96,7 @@ func TestListWithLabels(t *testing.T) {
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "")
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"}, "", "", false)
assert.NoError(t, err)

_, err = svc.List(context.Background())
Expand All @@ -115,7 +115,7 @@ func TestListWithState(t *testing.T) {
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened")
svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{}, "opened", "", false)
assert.NoError(t, err)

_, err = svc.List(context.Background())
Expand Down
15 changes: 12 additions & 3 deletions applicationset/services/scm_provider/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os"
pathpkg "path"

"github.com/argoproj/argo-cd/v2/applicationset/utils"
"github.com/hashicorp/go-retryablehttp"
"github.com/xanzy/go-gitlab"
)

Expand All @@ -19,21 +21,28 @@ type GitlabProvider struct {

var _ SCMProviderService = &GitlabProvider{}

func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups bool) (*GitlabProvider, error) {
func NewGitlabProvider(ctx context.Context, organization string, token string, url string, allBranches, includeSubgroups, insecure bool, scmRootCAPath string) (*GitlabProvider, error) {
// Undocumented environment variable to set a default token, to be used in testing to dodge anonymous rate limits.
if token == "" {
token = os.Getenv("GITLAB_TOKEN")
}
var client *gitlab.Client

tr := &http.Transport{
TLSClientConfig: utils.GetTlsConfig(scmRootCAPath, insecure),
}
retryClient := retryablehttp.NewClient()
retryClient.HTTPClient.Transport = tr

if url == "" {
var err error
client, err = gitlab.NewClient(token)
client, err = gitlab.NewClient(token, gitlab.WithHTTPClient(retryClient.HTTPClient))
if err != nil {
return nil, err
}
} else {
var err error
client, err = gitlab.NewClient(token, gitlab.WithBaseURL(url))
client, err = gitlab.NewClient(token, gitlab.WithBaseURL(url), gitlab.WithHTTPClient(retryClient.HTTPClient))
if err != nil {
return nil, err
}
Expand Down
14 changes: 7 additions & 7 deletions applicationset/services/scm_provider/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,10 +286,10 @@ func gitlabMockHandler(t *testing.T) func(http.ResponseWriter, *http.Request) {
}
func TestGitlabListRepos(t *testing.T) {
cases := []struct {
name, proto, url string
hasError, allBranches, includeSubgroups bool
branches []string
filters []v1alpha1.SCMProviderGeneratorFilter
name, proto, url string
hasError, allBranches, includeSubgroups, insecure bool
branches []string
filters []v1alpha1.SCMProviderGeneratorFilter
}{
{
name: "blank protocol",
Expand Down Expand Up @@ -323,7 +323,7 @@ func TestGitlabListRepos(t *testing.T) {
}))
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups)
provider, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, c.allBranches, c.includeSubgroups, c.insecure, "")
rawRepos, err := ListRepos(context.Background(), provider, c.filters, c.proto)
if c.hasError {
assert.NotNil(t, err)
Expand Down Expand Up @@ -352,7 +352,7 @@ func TestGitlabHasPath(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gitlabMockHandler(t)(w, r)
}))
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true)
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, false, "")
repo := &Repository{
Organization: "test-argocd-proton",
Repository: "argocd",
Expand Down Expand Up @@ -398,7 +398,7 @@ func TestGitlabGetBranches(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gitlabMockHandler(t)(w, r)
}))
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true)
host, _ := NewGitlabProvider(context.Background(), "test-argocd-proton", "", ts.URL, false, true, false, "")

repo := &Repository{
RepositoryId: 27084533,
Expand Down
38 changes: 38 additions & 0 deletions applicationset/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package utils

import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"os"
"reflect"
"regexp"
"sort"
Expand Down Expand Up @@ -406,3 +409,38 @@ func SanitizeName(name string) string {

return strings.Trim(name, "-.")
}

func getTlsConfigWithCACert(scmRootCAPath string) *tls.Config {

tlsConfig := &tls.Config{}

if scmRootCAPath != "" {
_, err := os.Stat(scmRootCAPath)
if os.IsNotExist(err) {
log.Errorf("scmRootCAPath '%s' specified does not exist: %s", scmRootCAPath, err)
return tlsConfig
}
rootCA, err := os.ReadFile(scmRootCAPath)
if err != nil {
log.Errorf("error reading certificate from file '%s', proceeding without custom rootCA : %s", scmRootCAPath, err)
return tlsConfig
}
certPool := x509.NewCertPool()
ok := certPool.AppendCertsFromPEM([]byte(rootCA))
if !ok {
log.Errorf("failed to append certificates from PEM: proceeding without custom rootCA")
} else {
tlsConfig.RootCAs = certPool
}
}
return tlsConfig
}

func GetTlsConfig(scmRootCAPath string, insecure bool) *tls.Config {
tlsConfig := getTlsConfigWithCACert(scmRootCAPath)

if insecure {
tlsConfig.InsecureSkipVerify = true
}
return tlsConfig
}
Loading