diff --git a/applicationset/controllers/requeue_after_test.go b/applicationset/controllers/requeue_after_test.go index da6b0b10b47df..6db6145af5348 100644 --- a/applicationset/controllers/requeue_after_test.go +++ b/applicationset/controllers/requeue_after_test.go @@ -60,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{}, "", []string{""}), + "SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), generators.SCMAuthProviders{}, "", []string{""}, true), "ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"), - "PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}), + "PullRequest": generators.NewPullRequestGenerator(k8sClient, generators.SCMAuthProviders{}, "", []string{""}, true), } nestedGenerators := map[string]generators.Generator{ diff --git a/applicationset/generators/pull_request.go b/applicationset/generators/pull_request.go index c024f1b723919..c1dfd5ed978e9 100644 --- a/applicationset/generators/pull_request.go +++ b/applicationset/generators/pull_request.go @@ -27,14 +27,16 @@ type PullRequestGenerator struct { auth SCMAuthProviders scmRootCAPath string allowedSCMProviders []string + enableSCMProviders bool } -func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string) Generator { +func NewPullRequestGenerator(client client.Client, auth SCMAuthProviders, scmRootCAPath string, allowedScmProviders []string, enableSCMProviders bool) Generator { g := &PullRequestGenerator{ client: client, auth: auth, scmRootCAPath: scmRootCAPath, allowedSCMProviders: allowedScmProviders, + enableSCMProviders: enableSCMProviders, } g.selectServiceProviderFunc = g.selectServiceProvider return g @@ -66,7 +68,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha ctx := context.Background() svc, err := g.selectServiceProviderFunc(ctx, appSetGenerator.PullRequest, applicationSetInfo) if err != nil { - return nil, fmt.Errorf("failed to select pull request service provider: %v", err) + return nil, fmt.Errorf("failed to select pull request service provider: %w", err) } pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters) @@ -121,17 +123,18 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha // selectServiceProvider selects the provider to get pull requests from the configuration func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, generatorConfig *argoprojiov1alpha1.PullRequestGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) { + if !g.enableSCMProviders { + return nil, ErrSCMProvidersDisabled + } + if err := ScmProviderAllowed(applicationSetInfo, generatorConfig, g.allowedSCMProviders); err != nil { + return nil, fmt.Errorf("scm provider not allowed: %w", err) + } + if generatorConfig.Github != nil { - if !ScmProviderAllowed(applicationSetInfo, generatorConfig.Github.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Github.API) - } return g.github(ctx, generatorConfig.Github, applicationSetInfo) } if generatorConfig.GitLab != nil { providerConfig := generatorConfig.GitLab - if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API) - } token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) if err != nil { return nil, fmt.Errorf("error fetching Secret token: %v", err) @@ -140,9 +143,6 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera } if generatorConfig.Gitea != nil { providerConfig := generatorConfig.Gitea - if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", generatorConfig.Gitea.API) - } token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace) if err != nil { return nil, fmt.Errorf("error fetching Secret token: %v", err) @@ -151,9 +151,6 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera } if generatorConfig.BitbucketServer != nil { providerConfig := generatorConfig.BitbucketServer - if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API) - } if providerConfig.BasicAuth != nil { password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) if err != nil { diff --git a/applicationset/generators/pull_request_test.go b/applicationset/generators/pull_request_test.go index 72017f522946e..9f4d3d0a9b693 100644 --- a/applicationset/generators/pull_request_test.go +++ b/applicationset/generators/pull_request_test.go @@ -278,7 +278,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { cases := []struct { name string providerConfig *argoprojiov1alpha1.PullRequestGenerator - expectedError string + expectedError error }{ { name: "Error Github", @@ -287,7 +287,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitlab", @@ -296,7 +296,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitea", @@ -305,7 +305,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Bitbucket", @@ -314,7 +314,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "failed to select pull request service provider: scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, } @@ -330,7 +330,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { "gitea.myorg.com", "bitbucket.myorg.com", "azuredevops.myorg.com", - }) + }, true) applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -346,7 +346,29 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) { _, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) assert.Error(t, err, "Must return an error") - assert.Equal(t, testCaseCopy.expectedError, err.Error()) + assert.ErrorAs(t, err, testCaseCopy.expectedError) }) } } + +func TestSCMProviderDisabled_PRGenerator(t *testing.T) { + generator := NewPullRequestGenerator(nil, SCMAuthProviders{}, "", []string{}, false) + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + PullRequest: &argoprojiov1alpha1.PullRequestGenerator{ + Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + }}, + }, + } + + _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + assert.ErrorIs(t, err, ErrSCMProvidersDisabled) +} diff --git a/applicationset/generators/scm_provider.go b/applicationset/generators/scm_provider.go index 3d7c930870eaf..42b7789be67f0 100644 --- a/applicationset/generators/scm_provider.go +++ b/applicationset/generators/scm_provider.go @@ -2,6 +2,7 @@ package generators import ( "context" + "errors" "fmt" "strings" "time" @@ -31,24 +32,26 @@ type SCMProviderGenerator struct { SCMAuthProviders scmRootCAPath string allowedSCMProviders []string + enableSCMProviders bool } type SCMAuthProviders struct { GitHubApps github_app_auth.Credentials } -func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string) Generator { +func NewSCMProviderGenerator(client client.Client, providers SCMAuthProviders, scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool) Generator { return &SCMProviderGenerator{ client: client, SCMAuthProviders: providers, scmRootCAPath: scmRootCAPath, allowedSCMProviders: allowedSCMProviders, + enableSCMProviders: enableSCMProviders, } } // Testing generator func NewTestSCMProviderGenerator(overrideProvider scm_provider.SCMProviderService) Generator { - return &SCMProviderGenerator{overrideProvider: overrideProvider} + return &SCMProviderGenerator{overrideProvider: overrideProvider, enableSCMProviders: true} } func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration { @@ -65,14 +68,34 @@ func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.A return &appSetGenerator.SCMProvider.Template } -func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, url string, allowedScmProviders []string) bool { +var ErrSCMProvidersDisabled = errors.New("scm providers are disabled") + +type ErrDisallowedSCMProvider struct { + Provider string + Allowed []string +} + +func NewErrDisallowedSCMProvider(provider string, allowed []string) ErrDisallowedSCMProvider { + return ErrDisallowedSCMProvider{ + Provider: provider, + Allowed: allowed, + } +} + +func (e ErrDisallowedSCMProvider) Error() string { + return fmt.Sprintf("scm provider %q not allowed, must use one of the following: %s", e.Provider, strings.Join(e.Allowed, ", ")) +} + +func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, generator SCMGeneratorWithCustomApiUrl, allowedScmProviders []string) error { + url := generator.CustomApiUrl() + if url == "" || len(allowedScmProviders) == 0 { - return true + return nil } for _, allowedScmProvider := range allowedScmProviders { if url == allowedScmProvider { - return true + return nil } } @@ -80,9 +103,9 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, u common.SecurityField: common.SecurityMedium, "applicationset": applicationSetInfo.Name, "appSetNamespace": applicationSetInfo.Namespace, - }).Debugf("attempted to use disallowed SCM %q", url) + }).Debugf("attempted to use disallowed SCM %q, must use one of the following: %s", url, strings.Join(allowedScmProviders, ", ")) - return false + return NewErrDisallowedSCMProvider(url, allowedScmProviders) } func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) ([]map[string]interface{}, error) { @@ -94,26 +117,28 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return nil, EmptyAppSetGeneratorError } - ctx := context.Background() + if !g.enableSCMProviders { + return nil, ErrSCMProvidersDisabled + } // Create the SCM provider helper. providerConfig := appSetGenerator.SCMProvider + + if err := ScmProviderAllowed(applicationSetInfo, providerConfig, g.allowedSCMProviders); err != nil { + return nil, fmt.Errorf("scm provider not allowed: %w", err) + } + + ctx := context.Background() var provider scm_provider.SCMProviderService if g.overrideProvider != nil { provider = g.overrideProvider } else if providerConfig.Github != nil { - if !ScmProviderAllowed(applicationSetInfo, providerConfig.Github.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Github.API) - } var err error provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo) if err != nil { return nil, fmt.Errorf("scm provider: %w", err) } } else if providerConfig.Gitlab != nil { - if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitlab.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitlab.API) - } token, err := g.getSecretRef(ctx, providerConfig.Gitlab.TokenRef, applicationSetInfo.Namespace) if err != nil { return nil, fmt.Errorf("error fetching Gitlab token: %v", err) @@ -123,9 +148,6 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return nil, fmt.Errorf("error initializing Gitlab service: %v", err) } } else if providerConfig.Gitea != nil { - if !ScmProviderAllowed(applicationSetInfo, providerConfig.Gitea.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.Gitea.API) - } token, err := g.getSecretRef(ctx, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace) if err != nil { return nil, fmt.Errorf("error fetching Gitea token: %v", err) @@ -136,9 +158,6 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha } } else if providerConfig.BitbucketServer != nil { providerConfig := providerConfig.BitbucketServer - if !ScmProviderAllowed(applicationSetInfo, providerConfig.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.API) - } var scmError error if providerConfig.BasicAuth != nil { password, err := g.getSecretRef(ctx, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace) @@ -153,9 +172,6 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha return nil, fmt.Errorf("error initializing Bitbucket Server service: %v", scmError) } } else if providerConfig.AzureDevOps != nil { - if !ScmProviderAllowed(applicationSetInfo, providerConfig.AzureDevOps.API, g.allowedSCMProviders) { - return nil, fmt.Errorf("scm provider not allowed: %s", providerConfig.AzureDevOps.API) - } token, err := g.getSecretRef(ctx, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace) if err != nil { return nil, fmt.Errorf("error fetching Azure Devops access token: %v", err) diff --git a/applicationset/generators/scm_provider_test.go b/applicationset/generators/scm_provider_test.go index 4dcb8fdf3ce6f..c438aa8f646fe 100644 --- a/applicationset/generators/scm_provider_test.go +++ b/applicationset/generators/scm_provider_test.go @@ -174,7 +174,7 @@ func TestSCMProviderGenerateParams(t *testing.T) { mockProvider := &scm_provider.MockProvider{ Repos: testCaseCopy.repos, } - scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider} + scmGenerator := &SCMProviderGenerator{overrideProvider: mockProvider, enableSCMProviders: true} applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "set", @@ -205,7 +205,7 @@ func TestAllowedSCMProvider(t *testing.T) { cases := []struct { name string providerConfig *argoprojiov1alpha1.SCMProviderGenerator - expectedError string + expectedError error }{ { name: "Error Github", @@ -214,7 +214,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitlab", @@ -223,7 +223,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Gitea", @@ -232,7 +232,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error Bitbucket", @@ -241,7 +241,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, { name: "Error AzureDevops", @@ -250,7 +250,7 @@ func TestAllowedSCMProvider(t *testing.T) { API: "https://myservice.mynamespace.svc.cluster.local", }, }, - expectedError: "scm provider not allowed: https://myservice.mynamespace.svc.cluster.local", + expectedError: &ErrDisallowedSCMProvider{}, }, } @@ -260,13 +260,16 @@ func TestAllowedSCMProvider(t *testing.T) { t.Run(testCaseCopy.name, func(t *testing.T) { t.Parallel() - scmGenerator := &SCMProviderGenerator{allowedSCMProviders: []string{ - "github.myorg.com", - "gitlab.myorg.com", - "gitea.myorg.com", - "bitbucket.myorg.com", - "azuredevops.myorg.com", - }} + scmGenerator := &SCMProviderGenerator{ + allowedSCMProviders: []string{ + "github.myorg.com", + "gitlab.myorg.com", + "gitea.myorg.com", + "bitbucket.myorg.com", + "azuredevops.myorg.com", + }, + enableSCMProviders: true, + } applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ @@ -282,7 +285,29 @@ func TestAllowedSCMProvider(t *testing.T) { _, err := scmGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) assert.Error(t, err, "Must return an error") - assert.Equal(t, testCaseCopy.expectedError, err.Error()) + assert.ErrorAs(t, err, testCaseCopy.expectedError) }) } } + +func TestSCMProviderDisabled_SCMGenerator(t *testing.T) { + generator := &SCMProviderGenerator{enableSCMProviders: false} + + applicationSetInfo := argoprojiov1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "set", + }, + Spec: argoprojiov1alpha1.ApplicationSetSpec{ + Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{ + SCMProvider: &argoprojiov1alpha1.SCMProviderGenerator{ + Github: &argoprojiov1alpha1.SCMProviderGeneratorGithub{ + API: "https://myservice.mynamespace.svc.cluster.local", + }, + }, + }}, + }, + } + + _, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo) + assert.ErrorIs(t, err, ErrSCMProvidersDisabled) +} diff --git a/applicationset/generators/scm_utils.go b/applicationset/generators/scm_utils.go new file mode 100644 index 0000000000000..51ac99d9b7e49 --- /dev/null +++ b/applicationset/generators/scm_utils.go @@ -0,0 +1,5 @@ +package generators + +type SCMGeneratorWithCustomApiUrl interface { + CustomApiUrl() string +} diff --git a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go index 9939cf6a4972c..9adbc3e64a685 100644 --- a/cmd/argocd-applicationset-controller/commands/applicationset_controller.go +++ b/cmd/argocd-applicationset-controller/commands/applicationset_controller.go @@ -65,6 +65,7 @@ func NewCommand() *cobra.Command { allowedScmProviders []string globalPreservedAnnotations []string globalPreservedLabels []string + enableScmProviders bool ) scheme := runtime.NewScheme() _ = clientgoscheme.AddToScheme(scheme) @@ -107,8 +108,8 @@ func NewCommand() *cobra.Command { // If the applicationset-namespaces contains only one namespace it corresponds to the current namespace if len(applicationSetNamespaces) == 1 { watchedNamespace = (applicationSetNamespaces)[0] - } else if len(allowedScmProviders) == 0 { - log.Error("When enabling applicationset in any namespace using applicationset-namespaces, allowed-scm-providers is required") + } else if enableScmProviders && len(allowedScmProviders) == 0 { + log.Error("When enabling applicationset in any namespace using applicationset-namespaces, you must either set --enable-scm-providers=false or specify --allowed-scm-providers") os.Exit(1) } @@ -162,9 +163,9 @@ func NewCommand() *cobra.Command { "List": generators.NewListGenerator(), "Clusters": generators.NewClusterGenerator(mgr.GetClient(), ctx, k8sClient, namespace), "Git": generators.NewGitGenerator(argoCDService), - "SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders), + "SCMProvider": generators.NewSCMProviderGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders), "ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace), - "PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders), + "PullRequest": generators.NewPullRequestGenerator(mgr.GetClient(), scmAuth, scmRootCAPath, allowedScmProviders, enableScmProviders), "Plugin": generators.NewPluginGenerator(mgr.GetClient(), ctx, k8sClient, namespace), } @@ -247,7 +248,8 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&debugLog, "debug", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DEBUG", false), "Print debug logs. Takes precedence over loglevel") command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json") command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") - command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed scm providers. (Default: Empty = all)") + command.Flags().StringSliceVar(&allowedScmProviders, "allowed-scm-providers", env.StringsFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS", []string{}, ","), "The list of allowed custom SCM provider API URLs. This restriction does not apply to SCM or PR generators which do not accept a custom API URL. (Default: Empty = all)") + command.Flags().BoolVar(&enableScmProviders, "enable-scm-providers", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS", true), "Enable retrieving information from SCM providers, used by the SCM and PR generators (Default: true)") command.Flags().BoolVar(&dryRun, "dry-run", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_DRY_RUN", false), "Enable dry run mode") command.Flags().BoolVar(&enableProgressiveSyncs, "enable-progressive-syncs", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_PROGRESSIVE_SYNCS", false), "Enable use of the experimental progressive syncs feature.") command.Flags().BoolVar(&enableNewGitFileGlobbing, "enable-new-git-file-globbing", env.ParseBoolFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_NEW_GIT_FILE_GLOBBING", false), "Enable new globbing in Git files generator.") diff --git a/docs/operator-manual/applicationset/Appset-Any-Namespace.md b/docs/operator-manual/applicationset/Appset-Any-Namespace.md index 494b36dbdcf36..61716414aeb69 100644 --- a/docs/operator-manual/applicationset/Appset-Any-Namespace.md +++ b/docs/operator-manual/applicationset/Appset-Any-Namespace.md @@ -53,7 +53,6 @@ spec: Therefore administrator must restrict the urls of the allowed SCM Providers (example: `https://git.mydomain.com/,https://gitlab.mydomain.com/`) by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allowed.scm.providers`. If another url is used, it will be rejected by the applicationset controller. - For example: ```yaml apiVersion: v1 @@ -64,7 +63,14 @@ data: applicationsetcontroller.allowed.scm.providers: https://git.mydomain.com/,https://gitlab.mydomain.com/ ``` -> Please note url used in the `api` field of the `ApplicationSet` must match the url declared by the Administrator including the protocol +!!! note + Please note url used in the `api` field of the `ApplicationSet` must match the url declared by the Administrator including the protocol + +!!! warning + The allow-list only applies to SCM providers for which the user may configure a custom `api`. Where an SCM or PR + generator does not accept a custom API URL, the provider is implicitly allowed. + +If you do not intend to allow users to use the SCM or PR generators, you can disable them entirely by setting the environment variable `ARGOCD_APPLICATIONSET_CONTROLLER_ALLOW_SCM_PROVIDERS` to argocd-cmd-params-cm `applicationsetcontroller.allow.scm.providers` to `false`. ### Overview diff --git a/docs/operator-manual/argocd-cmd-params-cm.yaml b/docs/operator-manual/argocd-cmd-params-cm.yaml index d4a754f0e44b9..7d38506d0b7ec 100644 --- a/docs/operator-manual/argocd-cmd-params-cm.yaml +++ b/docs/operator-manual/argocd-cmd-params-cm.yaml @@ -184,6 +184,8 @@ data: # sending secrets from `tokenRef`s to disallowed `api` domains. # The url used in the scm generator must exactly match one in the list applicationsetcontroller.allowed.scm.providers: "https://git.example.com/,https://gitlab.example.com/" + # To disable SCM providers entirely (i.e. disable the SCM and PR generators), set this to "false". Default is "true". + applicationsetcontroller.enable.scm.providers: "false" ## Argo CD Notifications Controller Properties # Set the logging level. One of: debug|info|warn|error (default "info") diff --git a/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml b/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml index 8de4d68bd657d..b24124ccb833f 100644 --- a/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml +++ b/manifests/base/applicationset-controller/argocd-applicationset-controller-deployment.yaml @@ -151,6 +151,12 @@ spec: name: argocd-cmd-params-cm key: applicationsetcontroller.allowed.scm.providers optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + name: argocd-cmd-params-cm + key: applicationsetcontroller.enable.scm.providers + optional: true volumeMounts: - mountPath: /app/config/ssh name: ssh-known-hosts diff --git a/manifests/core-install.yaml b/manifests/core-install.yaml index 2d8afc40dcfb0..8456f5c3ef430 100644 --- a/manifests/core-install.yaml +++ b/manifests/core-install.yaml @@ -20742,6 +20742,12 @@ spec: key: applicationsetcontroller.allowed.scm.providers name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller diff --git a/manifests/ha/install.yaml b/manifests/ha/install.yaml index e8bf0f01b6ebb..0051211e1b4bb 100644 --- a/manifests/ha/install.yaml +++ b/manifests/ha/install.yaml @@ -21998,6 +21998,12 @@ spec: key: applicationsetcontroller.allowed.scm.providers name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller diff --git a/manifests/ha/namespace-install.yaml b/manifests/ha/namespace-install.yaml index 7dec65a0cddc5..b1f5f0d68a815 100644 --- a/manifests/ha/namespace-install.yaml +++ b/manifests/ha/namespace-install.yaml @@ -1654,6 +1654,12 @@ spec: key: applicationsetcontroller.allowed.scm.providers name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller diff --git a/manifests/install.yaml b/manifests/install.yaml index a23138952e84f..69ea78fa5c058 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -21093,6 +21093,12 @@ spec: key: applicationsetcontroller.allowed.scm.providers name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 4ba2c7a8e7696..e19878573725d 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -749,6 +749,12 @@ spec: key: applicationsetcontroller.allowed.scm.providers name: argocd-cmd-params-cm optional: true + - name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_SCM_PROVIDERS + valueFrom: + configMapKeyRef: + key: applicationsetcontroller.enable.scm.providers + name: argocd-cmd-params-cm + optional: true image: quay.io/argoproj/argocd:latest imagePullPolicy: Always name: argocd-applicationset-controller diff --git a/pkg/apis/application/v1alpha1/applicationset_types.go b/pkg/apis/application/v1alpha1/applicationset_types.go index 23c8d5954f4d0..99db8124e51ea 100644 --- a/pkg/apis/application/v1alpha1/applicationset_types.go +++ b/pkg/apis/application/v1alpha1/applicationset_types.go @@ -432,6 +432,22 @@ type SCMProviderGenerator struct { // Values contains key/value pairs which are passed directly as parameters to the template Values map[string]string `json:"values,omitempty" protobuf:"bytes,11,name=values"` AWSCodeCommit *SCMProviderGeneratorAWSCodeCommit `json:"awsCodeCommit,omitempty" protobuf:"bytes,12,opt,name=awsCodeCommit"` + // If you add a new SCM provider, update CustomApiUrl below. +} + +func (g *SCMProviderGenerator) CustomApiUrl() string { + if g.Github != nil { + return g.Github.API + } else if g.Gitlab != nil { + return g.Gitlab.API + } else if g.Gitea != nil { + return g.Gitea.API + } else if g.BitbucketServer != nil { + return g.BitbucketServer.API + } else if g.AzureDevOps != nil { + return g.AzureDevOps.API + } + return "" } // SCMProviderGeneratorGitea defines a connection info specific to Gitea. @@ -574,6 +590,29 @@ type PullRequestGenerator struct { Bitbucket *PullRequestGeneratorBitbucket `json:"bitbucket,omitempty" protobuf:"bytes,8,opt,name=bitbucket"` // Additional provider to use and config for it. AzureDevOps *PullRequestGeneratorAzureDevOps `json:"azuredevops,omitempty" protobuf:"bytes,9,opt,name=azuredevops"` + // If you add a new SCM provider, update CustomApiUrl below. +} + +func (p *PullRequestGenerator) CustomApiUrl() string { + if p.Github != nil { + return p.Github.API + } + if p.GitLab != nil { + return p.GitLab.API + } + if p.Gitea != nil { + return p.Gitea.API + } + if p.BitbucketServer != nil { + return p.BitbucketServer.API + } + if p.Bitbucket != nil { + return p.Bitbucket.API + } + if p.AzureDevOps != nil { + return p.AzureDevOps.API + } + return "" } // PullRequestGeneratorGitea defines connection info specific to Gitea. diff --git a/test/e2e/applicationset_test.go b/test/e2e/applicationset_test.go index d6efbbcd86117..f56f9f552e9f6 100644 --- a/test/e2e/applicationset_test.go +++ b/test/e2e/applicationset_test.go @@ -1706,7 +1706,7 @@ func TestSCMProviderGeneratorSCMProviderNotAllowed(t *testing.T) { // app should be listed output, err := fixture.RunCli("appset", "get", "scm-provider-generator-scm-provider-not-allowed") assert.NoError(t, err) - assert.Contains(t, output, "scm provider not allowed: http://myservice.mynamespace.svc.cluster.local") + assert.Contains(t, output, "scm provider not allowed") }) } @@ -2113,7 +2113,7 @@ func TestPullRequestGeneratorNotAllowedSCMProvider(t *testing.T) { // app should be listed output, err := fixture.RunCli("appset", "get", "pull-request-generator-not-allowed-scm") assert.NoError(t, err) - assert.Contains(t, output, "failed to select pull request service provider: scm provider not allowed: http://myservice.mynamespace.svc.cluster.local") + assert.Contains(t, output, "scm provider not allowed") }) }