Skip to content

Commit

Permalink
Add support for tls self signed certs in AppSet Gitlab SCM Provider (#…
Browse files Browse the repository at this point in the history
…985)

* add support for tls self signed certs in AppSet Gitlab SCM Provider

Signed-off-by: ishitasequeira <[email protected]>

* add e2e test

Signed-off-by: ishitasequeira <[email protected]>

* add unit tests

Signed-off-by: ishitasequeira <[email protected]>

* renamed field ScmRootCaPath to SCMRootCaPath

Signed-off-by: ishitasequeira <[email protected]>

* Add documentation and address comments

Signed-off-by: ishitasequeira <[email protected]>

* Address comments

Signed-off-by: ishitasequeira <[email protected]>

---------

Signed-off-by: ishitasequeira <[email protected]>
  • Loading branch information
ishitasequeira authored Sep 6, 2023
1 parent 8ddbce3 commit 297702f
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 11 deletions.
3 changes: 3 additions & 0 deletions api/v1beta1/argocd_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ type ArgoCDApplicationSet struct {
LogLevel string `json:"logLevel,omitempty"`

WebhookServer WebhookServerSpec `json:"webhookServer,omitempty"`

// SCMRootCAConfigMap is the name of the config map that stores the Gitlab SCM Provider's TLS certificate which will be mounted on the ApplicationSet Controller (optional).
SCMRootCAConfigMap string `json:"scmRootCAConfigMap,omitempty"`
}

// ArgoCDCASpec defines the CA options for ArgCD.
Expand Down
5 changes: 5 additions & 0 deletions bundle/manifests/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6655,6 +6655,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
3 changes: 3 additions & 0 deletions common/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ const (
// ArgoCDTLSCertsConfigMapName is the upstream hard-coded TLS certificate data ConfigMap name.
ArgoCDTLSCertsConfigMapName = "argocd-tls-certs-cm"

// ArgoCDAppSetGitlabSCMTLSCertsConfigMapName is the hard-coded ApplicationSet Gitlab SCM TLS certificate data ConfigMap name.
ArgoCDAppSetGitlabSCMTLSCertsConfigMapName = "argocd-appset-gitlab-scm-tls-certs-cm"

// ArgoCDRedisServerTLSSecretName is the name of the TLS secret for the redis-server
ArgoCDRedisServerTLSSecretName = "argocd-operator-redis-tls"

Expand Down
5 changes: 5 additions & 0 deletions config/crd/bases/argoproj.io_argocds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6646,6 +6646,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
39 changes: 36 additions & 3 deletions controllers/argocd/applicationset.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ import (
"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
)

const (
ApplicationSetGitlabSCMTlsCertPath = "/app/tls/scm/cert"
)

// getArgoApplicationSetCommand will return the command for the ArgoCD ApplicationSet component.
func getArgoApplicationSetCommand(cr *argoproj.ArgoCD) []string {
cmd := make([]string, 0)
Expand All @@ -46,6 +50,11 @@ func getArgoApplicationSetCommand(cr *argoproj.ArgoCD) []string {
cmd = append(cmd, "--loglevel")
cmd = append(cmd, getLogLevel(cr.Spec.ApplicationSet.LogLevel))

if cr.Spec.ApplicationSet.SCMRootCAConfigMap != "" {
cmd = append(cmd, "--scm-root-ca-path")
cmd = append(cmd, ApplicationSetGitlabSCMTlsCertPath)
}

// ApplicationSet command arguments provided by the user
extraArgs := cr.Spec.ApplicationSet.ExtraCommandArgs
err := isMergable(extraArgs, cmd)
Expand Down Expand Up @@ -144,9 +153,26 @@ func (r *ReconcileArgoCD) reconcileApplicationSetDeployment(cr *argoproj.ArgoCD,
},
},
}
addSCMGitlabVolumeMount := false
if scmRootCAConfigMapName := getSCMRootCAConfigMapName(cr); scmRootCAConfigMapName != "" {
cm := newConfigMapWithName(scmRootCAConfigMapName, cr)
if argoutil.IsObjectFound(r.Client, cr.Namespace, cr.Spec.ApplicationSet.SCMRootCAConfigMap, cm) {
addSCMGitlabVolumeMount = true
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{
Name: "appset-gitlab-scm-tls-cert",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
},
},
},
})
}
}

podSpec.Containers = []corev1.Container{
applicationSetContainer(cr),
applicationSetContainer(cr, addSCMGitlabVolumeMount),
}
AddSeccompProfileForOpenShift(r.Client, podSpec)

Expand Down Expand Up @@ -185,7 +211,7 @@ func (r *ReconcileArgoCD) reconcileApplicationSetDeployment(cr *argoproj.ArgoCD,

}

func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
func applicationSetContainer(cr *argoproj.ArgoCD, addSCMGitlabVolumeMount bool) corev1.Container {
// Global proxy env vars go first
appSetEnv := []corev1.EnvVar{{
Name: "NAMESPACE",
Expand All @@ -202,7 +228,7 @@ func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
// Environment specified in the CR take precedence over everything else
appSetEnv = argoutil.EnvMerge(appSetEnv, proxyEnvVars(), false)

return corev1.Container{
container := corev1.Container{
Command: getArgoApplicationSetCommand(cr),
Env: appSetEnv,
Image: getApplicationSetContainerImage(cr),
Expand Down Expand Up @@ -252,6 +278,13 @@ func applicationSetContainer(cr *argoproj.ArgoCD) corev1.Container {
RunAsNonRoot: boolPtr(true),
},
}
if addSCMGitlabVolumeMount {
container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{
Name: "appset-gitlab-scm-tls-cert",
MountPath: ApplicationSetGitlabSCMTlsCertPath,
})
}
return container
}

func (r *ReconcileArgoCD) reconcileApplicationSetServiceAccount(cr *argoproj.ArgoCD) (*corev1.ServiceAccount, error) {
Expand Down
36 changes: 30 additions & 6 deletions controllers/argocd/applicationset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"

argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
Expand Down Expand Up @@ -92,14 +93,14 @@ func TestReconcileApplicationSet_CreateDeployments(t *testing.T) {
deployment))

// Ensure the created Deployment has the expected properties
checkExpectedDeploymentValues(t, deployment, &sa, a)
checkExpectedDeploymentValues(t, r, deployment, &sa, a)
}

func checkExpectedDeploymentValues(t *testing.T, deployment *appsv1.Deployment, sa *corev1.ServiceAccount, a *argoproj.ArgoCD) {
func checkExpectedDeploymentValues(t *testing.T, r *ReconcileArgoCD, deployment *appsv1.Deployment, sa *corev1.ServiceAccount, a *argoproj.ArgoCD) {
assert.Equal(t, deployment.Spec.Template.Spec.ServiceAccountName, sa.ObjectMeta.Name)
appsetAssertExpectedLabels(t, &deployment.ObjectMeta)

want := []corev1.Container{applicationSetContainer(a)}
want := []corev1.Container{applicationSetContainer(a, false)}

if diff := cmp.Diff(want, deployment.Spec.Template.Spec.Containers); diff != "" {
t.Fatalf("failed to reconcile applicationset-controller deployment containers:\n%s", diff)
Expand Down Expand Up @@ -150,6 +151,19 @@ func checkExpectedDeploymentValues(t *testing.T, deployment *appsv1.Deployment,
},
}

if a.Spec.ApplicationSet.SCMRootCAConfigMap != "" && argoutil.IsObjectFound(r.Client, a.Namespace, common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName, a) {
volumes = append(volumes, corev1.Volume{
Name: "appset-gitlab-scm-tls-cert",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
},
},
},
})
}

if diff := cmp.Diff(volumes, deployment.Spec.Template.Spec.Volumes); diff != "" {
t.Fatalf("failed to reconcile applicationset-controller deployment volumes:\n%s", diff)
}
Expand Down Expand Up @@ -261,7 +275,7 @@ func TestReconcileApplicationSet_UpdateExistingDeployments(t *testing.T) {
deployment))

// Ensure the updated Deployment has the expected properties
checkExpectedDeploymentValues(t, deployment, &sa, a)
checkExpectedDeploymentValues(t, r, deployment, &sa, a)

}

Expand All @@ -287,7 +301,7 @@ func TestReconcileApplicationSet_Deployments_resourceRequirements(t *testing.T)
assert.Equal(t, deployment.Spec.Template.Spec.ServiceAccountName, sa.ObjectMeta.Name)
appsetAssertExpectedLabels(t, &deployment.ObjectMeta)

containerWant := []corev1.Container{applicationSetContainer(a)}
containerWant := []corev1.Container{applicationSetContainer(a, false)}

if diff := cmp.Diff(containerWant, deployment.Spec.Template.Spec.Containers); diff != "" {
t.Fatalf("failed to reconcile argocd-server deployment:\n%s", diff)
Expand Down Expand Up @@ -346,6 +360,14 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {
envVars: map[string]string{common.ArgoCDImageEnvName: "custom-env-image"},
expectedContainerImage: "custom-image:custom-version",
},
{
name: "ensure scm tls cert mount is present",
appSetField: &argoproj.ArgoCDApplicationSet{
SCMRootCAConfigMap: "test-scm-tls-mount",
},
envVars: map[string]string{common.ArgoCDImageEnvName: "custom-env-image"},
expectedContainerImage: "custom-env-image",
},
}

for _, test := range tests {
Expand All @@ -357,6 +379,8 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {

a := makeTestArgoCD()
r := makeTestReconciler(t, a)
cm := newConfigMapWithName(getCAConfigMapName(a), a)
r.Client.Create(context.Background(), cm, &client.CreateOptions{})

a.Spec.ApplicationSet = test.appSetField

Expand All @@ -374,7 +398,7 @@ func TestReconcileApplicationSet_Deployments_SpecOverride(t *testing.T) {

specImage := deployment.Spec.Template.Spec.Containers[0].Image
assert.Equal(t, test.expectedContainerImage, specImage)

checkExpectedDeploymentValues(t, r, deployment, &sa, a)
})
}

Expand Down
2 changes: 1 addition & 1 deletion controllers/argocd/argocd_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,6 @@ func (r *ReconcileArgoCD) Reconcile(ctx context.Context, request ctrl.Request) (
// SetupWithManager sets up the controller with the Manager.
func (r *ReconcileArgoCD) SetupWithManager(mgr ctrl.Manager) error {
bldr := ctrl.NewControllerManagedBy(mgr)
r.setResourceWatches(bldr, r.clusterResourceMapper, r.tlsSecretMapper, r.namespaceResourceMapper, r.clusterSecretResourceMapper)
r.setResourceWatches(bldr, r.clusterResourceMapper, r.tlsSecretMapper, r.namespaceResourceMapper, r.clusterSecretResourceMapper, r.applicationSetSCMTLSConfigMapMapper)
return bldr.Complete(r)
}
8 changes: 8 additions & 0 deletions controllers/argocd/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ func getCAConfigMapName(cr *argoproj.ArgoCD) string {
return nameWithSuffix(common.ArgoCDCASuffix, cr)
}

// getSCMRootCAConfigMapName will return the SCMRootCA ConfigMap name for the given ArgoCD ApplicationSet Controller.
func getSCMRootCAConfigMapName(cr *argoproj.ArgoCD) string {
if cr.Spec.ApplicationSet.SCMRootCAConfigMap != "" && len(cr.Spec.ApplicationSet.SCMRootCAConfigMap) > 0 {
return cr.Spec.ApplicationSet.SCMRootCAConfigMap
}
return ""
}

// getConfigManagementPlugins will return the config management plugins for the given ArgoCD.
func getConfigManagementPlugins(cr *argoproj.ArgoCD) string {
plugins := common.ArgoCDDefaultConfigManagementPlugins
Expand Down
28 changes: 28 additions & 0 deletions controllers/argocd/custommapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,31 @@ func (r *ReconcileArgoCD) clusterSecretResourceMapper(o client.Object) []reconci

return result
}

// applicationSetSCMTLSConfigMapMapper maps a watch event on a configmap with name "argocd-appset-gitlab-scm-tls-certs-cm",
// back to the ArgoCD object that we want to reconcile.
func (r *ReconcileArgoCD) applicationSetSCMTLSConfigMapMapper(o client.Object) []reconcile.Request {
var result = []reconcile.Request{}

if o.GetName() == common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName {
argocds := &argoproj.ArgoCDList{}
if err := r.Client.List(context.TODO(), argocds, &client.ListOptions{Namespace: o.GetNamespace()}); err != nil {
return result
}

if len(argocds.Items) != 1 {
return result
}

argocd := argocds.Items[0]
namespacedName := client.ObjectKey{
Name: argocd.Name,
Namespace: argocd.Namespace,
}
result = []reconcile.Request{
{NamespacedName: namespacedName},
}
}

return result
}
8 changes: 7 additions & 1 deletion controllers/argocd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ func removeString(slice []string, s string) []string {
}

// setResourceWatches will register Watches for each of the supported Resources.
func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResourceMapper, tlsSecretMapper, namespaceResourceMapper, clusterSecretResourceMapper handler.MapFunc) *builder.Builder {
func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResourceMapper, tlsSecretMapper, namespaceResourceMapper, clusterSecretResourceMapper, applicationSetGitlabSCMTLSConfigMapMapper handler.MapFunc) *builder.Builder {

deploymentConfigPred := predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
Expand Down Expand Up @@ -1046,12 +1046,18 @@ func (r *ReconcileArgoCD) setResourceWatches(bldr *builder.Builder, clusterResou

clusterSecretResourceHandler := handler.EnqueueRequestsFromMapFunc(clusterSecretResourceMapper)

appSetGitlabSCMTLSConfigMapHandler := handler.EnqueueRequestsFromMapFunc(applicationSetGitlabSCMTLSConfigMapMapper)

tlsSecretHandler := handler.EnqueueRequestsFromMapFunc(tlsSecretMapper)

bldr.Watches(&source.Kind{Type: &v1.ClusterRoleBinding{}}, clusterResourceHandler)

bldr.Watches(&source.Kind{Type: &v1.ClusterRole{}}, clusterResourceHandler)

bldr.Watches(&source.Kind{Type: &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
Name: common.ArgoCDAppSetGitlabSCMTLSCertsConfigMapName,
}}}, appSetGitlabSCMTLSConfigMapHandler)

// Watch for secrets of type TLS that might be created by external processes
bldr.Watches(&source.Kind{Type: &corev1.Secret{Type: corev1.SecretTypeTLS}}, tlsSecretHandler)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6655,6 +6655,11 @@ spec:
to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
type: object
type: object
scmRootCAConfigMap:
description: SCMRootCAConfigMap is the name of the config map
that stores the Gitlab SCM Provider's TLS certificate which
will be mounted on the ApplicationSet Controller (optional).
type: string
version:
description: Version is the Argo CD ApplicationSet image tag.
(optional)
Expand Down
19 changes: 19 additions & 0 deletions docs/reference/argocd.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Resources | [Empty] | The container compute resources.
LogLevel | info | The log level to be used by the ArgoCD Application Controller component. Valid options are debug, info, error, and warn.
LogFormat | text | The log format to be used by the ArgoCD Application Controller component. Valid options are text or json.
ParallelismLimit | 10 | The kubectl parallelism limit to set for the controller (`--kubectl-parallelism-limit` flag)
SCMRootCAConfigMap (#add-tls-certificate-for-gitlab-scm-provider-to-applicationsets-controller) | [Empty] | The name of the config map that stores the Gitlab SCM Provider's TLS certificate which will be mounted on the ApplicationSet Controller at `"/app/tls/scm/cert"` path.

### ApplicationSet Controller Example

Expand Down Expand Up @@ -119,6 +120,24 @@ spec:
- bar
```

### Add Self signed TLS Certificate for Gitlab SCM Provider to ApplicationSets Controller

ApplicationSetController added a new option `--scm-root-ca-path` and expects the self-signed TLS certificate to be mounted on the path specified and to be used for Gitlab SCM Provider and Gitlab Pull Request Provider. To set this option, you can store the certificate in the config map and specify the config map name using `spec.applicationSet.SCMRootCAConfigMap` in ArgoCD CR. When the parameter `spec.applicationSet.SCMRootCAConfigMap` is set in ArgoCD CR, the operator checks for ConfigMap in the same namespace as the ArgoCD instance and mounts the Certificate stored in ConfigMap to ApplicationSet Controller pods at the path `/app/tls/scm/cert`.

Below example shows how a user can add scmRootCaPath to the ApplicationSet controller.
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ArgoCD
metadata:
name: example-argocd
labels:
example: applicationset
spec:
applicationSet:
SCMRootCAConfigMap: example-gitlab-scm-tls-cert
```


## Config Management Plugins

Configuration to add a config management plugin. This property maps directly to the `configManagementPlugins` field in the `argocd-cm` ConfigMap.
Expand Down
Loading

0 comments on commit 297702f

Please sign in to comment.