From 525faf4646bb25d2777ecd8fb7c61b0b2335010a Mon Sep 17 00:00:00 2001 From: Donald Sebastian Leung Date: Wed, 17 Apr 2024 21:11:47 +0800 Subject: [PATCH 1/2] Add field in ArgoCD CR to enable web terminal Signed-off-by: Donald Sebastian Leung --- api/v1beta1/argocd_types.go | 9 +++++++++ api/v1beta1/zz_generated.deepcopy.go | 16 ++++++++++++++++ common/defaults.go | 3 +++ common/keys.go | 3 +++ config/crd/bases/argoproj.io_argocds.yaml | 9 +++++++++ controllers/argocd/configmap.go | 10 ++++++++++ controllers/argocd/configmap_test.go | 1 + 7 files changed, 51 insertions(+) diff --git a/api/v1beta1/argocd_types.go b/api/v1beta1/argocd_types.go index 0e97a6828..a5b93ea25 100644 --- a/api/v1beta1/argocd_types.go +++ b/api/v1beta1/argocd_types.go @@ -850,6 +850,9 @@ type ArgoCDSpec struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Version",xDescriptors={"urn:alm:descriptor:com.tectonic.ui:fieldGroup:ArgoCD","urn:alm:descriptor:com.tectonic.ui:text"} Version string `json:"version,omitempty"` + // WebTerminal defines the web terminal options for ArgoCD. + WebTerminal ArgoCDWebTerminalSpec `json:"webTerminal,omitempty"` + // Banner defines an additional banner to be displayed in Argo CD UI Banner *Banner `json:"banner,omitempty"` } @@ -957,6 +960,12 @@ type ArgoCDTLSSpec struct { InitialCerts map[string]string `json:"initialCerts,omitempty"` } +// ArgoCDWebTerminalSpec defines the web terminal options for ArgoCD. +type ArgoCDWebTerminalSpec struct { + // Enabled defines whether the web terminal is enabled + Enabled bool `json:"enabled"` +} + type SSHHostsSpec struct { // ExcludeDefaultHosts describes whether you would like to include the default // list of SSH Known Hosts provided by ArgoCD. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index dc2cb1a30..62613fb1d 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -871,6 +871,7 @@ func (in *ArgoCDSpec) DeepCopyInto(out *ArgoCDSpec) { (*in).DeepCopyInto(*out) } in.TLS.DeepCopyInto(&out.TLS) + out.WebTerminal = in.WebTerminal if in.Banner != nil { in, out := &in.Banner, &out.Banner *out = new(Banner) @@ -926,6 +927,21 @@ func (in *ArgoCDTLSSpec) DeepCopy() *ArgoCDTLSSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ArgoCDWebTerminalSpec) DeepCopyInto(out *ArgoCDWebTerminalSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ArgoCDWebTerminalSpec. +func (in *ArgoCDWebTerminalSpec) DeepCopy() *ArgoCDWebTerminalSpec { + if in == nil { + return nil + } + out := new(ArgoCDWebTerminalSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Banner) DeepCopyInto(out *Banner) { *out = *in diff --git a/common/defaults.go b/common/defaults.go index a06c9f319..d550505ec 100644 --- a/common/defaults.go +++ b/common/defaults.go @@ -145,6 +145,9 @@ const ( // ArgoCDDefaultKustomizeBuildOptions is the default kustomize build options. ArgoCDDefaultKustomizeBuildOptions = "" + // ArgoCDDefaultWebTerminalEnabled is the default web terminal enabled switch. + ArgoCDDefaultWebTerminalEnabled = "false" + // ArgoCDKeycloakImage is the default Keycloak Image used for the non-openshift platforms when not specified. ArgoCDKeycloakImage = "quay.io/keycloak/keycloak" diff --git a/common/keys.go b/common/keys.go index 916e44ae5..f0a1bab14 100644 --- a/common/keys.go +++ b/common/keys.go @@ -168,6 +168,9 @@ const ( // ArgoCDKeyUsersAnonymousEnabled is the configuration key for anonymous user access. ArgoCDKeyUsersAnonymousEnabled = "users.anonymous.enabled" + // ArgoCDKeyWebTerminalEnabled is the configuration key for enabling the web terminal. + ArgoCDKeyWebTerminalEnabled = "exec.enabled" + // ArgoCDDexImageEnvName is the environment variable used to get the image // to used for the Dex container. ArgoCDDexImageEnvName = "ARGOCD_DEX_IMAGE" diff --git a/config/crd/bases/argoproj.io_argocds.yaml b/config/crd/bases/argoproj.io_argocds.yaml index 8c4417191..a0c02987d 100644 --- a/config/crd/bases/argoproj.io_argocds.yaml +++ b/config/crd/bases/argoproj.io_argocds.yaml @@ -13798,6 +13798,15 @@ spec: description: Version is the tag to use with the ArgoCD container image for all ArgoCD components. type: string + webTerminal: + description: WebTerminal defines the web terminal options for ArgoCD. + properties: + enabled: + description: Enabled defines whether the web terminal is enabled + type: boolean + required: + - enabled + type: object type: object status: description: ArgoCDStatus defines the observed state of ArgoCD diff --git a/controllers/argocd/configmap.go b/controllers/argocd/configmap.go index 4cc23c0e1..9b259fd6f 100644 --- a/controllers/argocd/configmap.go +++ b/controllers/argocd/configmap.go @@ -113,6 +113,15 @@ func getKustomizeBuildOptions(cr *argoproj.ArgoCD) string { return kbo } +// getWebTerminalEnabled will return whether the web terminal is enabled for the given ArgoCD. +func getWebTerminalEnabled(cr *argoproj.ArgoCD) string { + wte := common.ArgoCDDefaultWebTerminalEnabled + if cr.Spec.WebTerminal.Enabled { + wte = "true" + } + return wte +} + // getOIDCConfig will return the OIDC configuration for the given ArgoCD. func getOIDCConfig(cr *argoproj.ArgoCD) string { config := common.ArgoCDDefaultOIDCConfig @@ -377,6 +386,7 @@ func (r *ReconcileArgoCD) reconcileArgoConfigMap(cr *argoproj.ArgoCD) error { cm.Data[common.ArgoCDKeyHelpChatURL] = getHelpChatURL(cr) cm.Data[common.ArgoCDKeyHelpChatText] = getHelpChatText(cr) cm.Data[common.ArgoCDKeyKustomizeBuildOptions] = getKustomizeBuildOptions(cr) + cm.Data[common.ArgoCDKeyWebTerminalEnabled] = getWebTerminalEnabled(cr) if len(cr.Spec.KustomizeVersions) > 0 { for _, kv := range cr.Spec.KustomizeVersions { diff --git a/controllers/argocd/configmap_test.go b/controllers/argocd/configmap_test.go index a928f6ef6..b82aed93f 100644 --- a/controllers/argocd/configmap_test.go +++ b/controllers/argocd/configmap_test.go @@ -164,6 +164,7 @@ func TestReconcileArgoCD_reconcileArgoConfigMap(t *testing.T) { "statusbadge.enabled": "false", "url": "https://argocd-server", "users.anonymous.enabled": "false", + "exec.enabled": "false", } cmdTests := []struct { From d76bca02ee3b106e6c53ba483cd89a9da956e897 Mon Sep 17 00:00:00 2001 From: Donald Sebastian Leung Date: Wed, 17 Apr 2024 21:55:35 +0800 Subject: [PATCH 2/2] Add RBAC for web terminal Signed-off-by: Donald Sebastian Leung --- controllers/argocd/policyrule.go | 25 +++++++++++++++++++++---- controllers/argocd/role.go | 2 +- controllers/argocd/rolebinding.go | 2 +- controllers/argocd/service_account.go | 2 +- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/controllers/argocd/policyrule.go b/controllers/argocd/policyrule.go index c94e9e559..dead0c4b7 100644 --- a/controllers/argocd/policyrule.go +++ b/controllers/argocd/policyrule.go @@ -5,6 +5,7 @@ import ( "golang.org/x/mod/semver" + argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1" "github.com/argoproj-labs/argocd-operator/common" v1 "k8s.io/api/rbac/v1" @@ -81,8 +82,8 @@ func policyRuleForDexServer() []v1.PolicyRule { } } -func policyRuleForServer() []v1.PolicyRule { - return []v1.PolicyRule{ +func policyRuleForServer(cr *argoproj.ArgoCD) []v1.PolicyRule { + rules := []v1.PolicyRule{ { APIGroups: []string{ "*", @@ -159,6 +160,22 @@ func policyRuleForServer() []v1.PolicyRule { }, }, } + + if cr.Spec.WebTerminal.Enabled { + rules = append(rules, v1.PolicyRule{ + APIGroups: []string{ + "", + }, + Resources: []string{ + "pods/exec", + }, + Verbs: []string{ + "create", + }, + }) + } + + return rules } func policyRuleForNotificationsController() []v1.PolicyRule { @@ -316,7 +333,7 @@ func policyRuleForServerClusterRole() []v1.PolicyRule { } } -func getPolicyRuleList(client client.Client) []struct { +func getPolicyRuleList(client client.Client, cr *argoproj.ArgoCD) []struct { name string policyRule []v1.PolicyRule } { @@ -332,7 +349,7 @@ func getPolicyRuleList(client client.Client) []struct { policyRule: policyRuleForDexServer(), }, { name: common.ArgoCDServerComponent, - policyRule: policyRuleForServer(), + policyRule: policyRuleForServer(cr), }, { name: common.ArgoCDRedisHAComponent, policyRule: policyRuleForRedisHa(client), diff --git a/controllers/argocd/role.go b/controllers/argocd/role.go index b2394081b..d5a4af86a 100644 --- a/controllers/argocd/role.go +++ b/controllers/argocd/role.go @@ -64,7 +64,7 @@ func newClusterRole(name string, rules []v1.PolicyRule, cr *argoproj.ArgoCD) *v1 // reconcileRoles will ensure that all ArgoCD Service Accounts are configured. func (r *ReconcileArgoCD) reconcileRoles(cr *argoproj.ArgoCD) error { - params := getPolicyRuleList(r.Client) + params := getPolicyRuleList(r.Client, cr) for _, param := range params { if _, err := r.reconcileRole(param.name, param.policyRule, cr); err != nil { diff --git a/controllers/argocd/rolebinding.go b/controllers/argocd/rolebinding.go index 4e70dadc8..2f0090300 100644 --- a/controllers/argocd/rolebinding.go +++ b/controllers/argocd/rolebinding.go @@ -84,7 +84,7 @@ func newRoleBindingWithname(name string, cr *argoproj.ArgoCD) *v1.RoleBinding { // reconcileRoleBindings will ensure that all ArgoCD RoleBindings are configured. func (r *ReconcileArgoCD) reconcileRoleBindings(cr *argoproj.ArgoCD) error { - params := getPolicyRuleList(r.Client) + params := getPolicyRuleList(r.Client, cr) for _, param := range params { if err := r.reconcileRoleBinding(param.name, param.policyRule, cr); err != nil { diff --git a/controllers/argocd/service_account.go b/controllers/argocd/service_account.go index e7c477279..44ae8429b 100644 --- a/controllers/argocd/service_account.go +++ b/controllers/argocd/service_account.go @@ -58,7 +58,7 @@ func getServiceAccountName(crName, name string) string { // reconcileServiceAccounts will ensure that all ArgoCD Service Accounts are configured. func (r *ReconcileArgoCD) reconcileServiceAccounts(cr *argoproj.ArgoCD) error { - params := getPolicyRuleList(r.Client) + params := getPolicyRuleList(r.Client, cr) for _, param := range params { if err := r.reconcileServiceAccountPermissions(param.name, param.policyRule, cr); err != nil {