diff --git a/.gitignore b/.gitignore index 589f6f7..6acd017 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ dist/ dist-goreleaser/ bin/ install.yaml +*.DS_Store diff --git a/Makefile b/Makefile index 102342a..df7ad0d 100644 --- a/Makefile +++ b/Makefile @@ -112,7 +112,10 @@ run-backend: build goreman ## Run the api backend server. ##@ Build .PHONY: build -build: manifests generate fmt vet ## Build manager binary. +build: build-ui build-go ## Build the UI extension and the ephemeral-access binary. + +.PHONY: build-go +build-go: manifests generate fmt vet ## Build the ephemeral-access binary. go build -o bin/ephemeral-access cmd/main.go # If you wish to build the manager image targeting other platforms you can use the --platform flag. @@ -126,6 +129,29 @@ docker-build: ## Build docker image. docker-push: ## Push docker image. $(CONTAINER_TOOL) push ${IMG} +.PHONY: clean-ui +clean-ui: ## delete the extension.tar file. + find ${UI_DIR} -type f -name extension.tar -delete + +.PHONY: build-ui +build-ui: clean-ui ## build the Argo CD UI extension creating the ui/extension.tar file. + yarn --cwd ${UI_DIR} install + yarn --cwd ${UI_DIR} build + +.PHONY: update-openapi-ui +update-openapi-ui: build goreman ## Update the OpenAPI spec from the local server. + @echo "Starting the backend server" + goreman start backend & + sleep 5 + @echo "Downloading OpenAPI spec from dev server" + yarn --cwd ${UI_DIR} api:download + killall goreman || true + +.PHONY: codegen-ui +codegen-ui: ## Generate the UI API files based on the OpenAPI spec. + yarn --cwd ${UI_DIR} api:generate + + .PHONY: goreleaser-build-local goreleaser-build-local: goreleaser ## Run goreleaser build locally. Use to validate the goreleaser configuration. $(GORELEASER) build --snapshot --clean --single-target --verbose @@ -147,15 +173,8 @@ docker-buildx: ## Build and push docker image for the manager for cross-platform - $(CONTAINER_TOOL) buildx rm argocd-ephemeral-access-builder rm Dockerfile.cross -.PHONY: build-installer -build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. - mkdir -p dist - cd config/default && $(KUSTOMIZE) edit set image argoproj-labs/argocd-ephemeral-access=${IMG} - $(KUSTOMIZE) build config/default > dist/install.yaml - .PHONY: manifests-release manifests-release: generate manifests kustomize ## Generate the consolidated install.yaml with the release tag. - mkdir -p dist ./scripts/manifests-release.sh $(KUSTOMIZE) $(IMAGE_TAG) ##@ Deployment @@ -174,7 +193,7 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified .PHONY: deploy deploy: manifests-release ## Deploy distribution to the K8s cluster specified in ~/.kube/config. - $(KUBECTL) apply -f dist/install.yaml + $(KUBECTL) apply -f install.yaml .PHONY: undeploy undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. @@ -254,28 +273,6 @@ $(GOREMAN): $(LOCALBIN) generate-mocks: mockery ## Generate the mocks for the project as configured in .mockery.yaml $(MOCKERY) -.PHONY: clean-ui -clean-ui: - find ${UI_DIR} -type f -name extension.tar -delete - -.PHONY: update-openapi-ui -update-openapi-ui: build goreman ## Update the OpenAPI spec from the local server. - @echo "Starting the backend server" - goreman start backend & - sleep 5 - @echo "Downloading OpenAPI spec from dev server" - yarn --cwd ${UI_DIR} api:download - killall goreman || true - -.PHONY: codegen-ui -codegen-ui: ## Generate the UI API files based on the OpenAPI spec. - yarn --cwd ${UI_DIR} api:generate - -.PHONY: build-ui -build-ui: clean-ui ## Build the UI extension. - yarn --cwd ${UI_DIR} install - yarn --cwd ${UI_DIR} build - # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary (ideally with version) # $2 - package url which can be installed diff --git a/README.md b/README.md index 445c25e..e6d4a85 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,248 @@ # argocd-ephemeral-access -A kubernetes controller to manage Argo CD temporary access +## Overview + +This project provides an Argo CD extension to enable ephemeral access +in Argo CD UI. It can be viewed as something similar to the +functionality that `sudo` command provides as users can execute +actions that require higher permissions. The exact access the user is +allowed to be elevated to and for how long the access should be +granted are configurable. The elevated access are automatically +managed by creating and updating Argo CD AppProject roles. + +Note: This project requires that the Argo CD `Applications` are +associated with an `AppProjects` different than `default`. + +## Installation + +The ephemeral-access functionality is provided by the following +components that needs to be configured properly to achieve the desired +behaviour: + +- ui: Argo CD UI extension that provides users with the functionality + to request elevated access to an Argo CD Application. +- backend: Serves the REST API used by the UI extension. +- controller: Responsible for reconciling the AccessRequest resource. + +### Installing the Backend and the Controller + +We provide a consolidated `install.yaml` asset file in every release. +The `install.yaml` file contains all the resources required to run the +backend service and the controller. Check the latest release in the +[releases page][1] and replace the `DESIRED_VERSION` in the command +below: + +```bash +kubectl apply -f https://github.com/argoproj-labs/argocd-ephemeral-access/releases/download//install.yaml +``` + +This command will create a new namespace `argocd-ephemeral-access` and +deploy the necessary resources in it. + +All configurations available for the backend and controller are +provided as part of the dedicated configmap for each of those +components. To modify the configuration, the suggested method is to +create a kustomize overlay and patch the configmaps. Refer to the +following files for a quick reference about the available fields as +well as the documentation: + +- [Backend configuration][3] +- [Controller configuration][4] + +### Install UI extension + +The UI extension needs to be installed by mounting the React component +in Argo CD API server. This process can be automated by using the +[argocd-extension-installer][2]. This installation method will run an +init container that will download, extract and place the file in the +correct location. + +The yaml file below is an example of how to define a kustomize patch +to install this UI extension: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argocd-server +spec: + template: + spec: + initContainers: + - name: extension-metrics + image: quay.io/argoprojlabs/argocd-extension-installer:v0.0.8@sha256:e7cb054207620566286fce2d809b4f298a72474e0d8779ffa8ec92c3b630f054 + env: + - name: EXTENSION_URL + value: https://github.com/argoproj-labs/argocd-ephemeral-access/releases/download/v0.0.1/extension.tar.gz + - name: EXTENSION_CHECKSUM_URL + value: https://github.com/argoproj-labs/argocd-ephemeral-access/releases/download/v0.0.1/extension_checksums.txt + volumeMounts: + - name: extensions + mountPath: /tmp/extensions/ + securityContext: + runAsUser: 1000 + allowPrivilegeEscalation: false + containers: + - name: argocd-server + volumeMounts: + - name: extensions + mountPath: /tmp/extensions/ + volumes: + - name: extensions + emptyDir: {} +``` + +## How it Works + +This project provides a set of CRDs that are used to configure the +behaviour of how the Argo CD access can be elevated. +The diagram below describes how the different components interact +which each CRD: + +![Ephemeral Access Diagram](docs/assets/EphemeralAccessExtensionDiagram.png) + +The CRDs provided as part of this project are described below: + +### AccessBinding + +The `AccessBinding` resource is used by the backend service in order +to authorize the incoming access request. The authorization is based +on the `groups` claim that are associated with the user token once +authenticated by the OIDC provider. The backend service will try to +match each entry of the user's `groups` claim with the value provided +in the `.spec.subjects` list. At least one entry needs to match for +the user to be allowed to have their access elavated to the role +defined in the `spec.roleTemplateRef.name`. + +The `.spec.subjects` list are templated fields and accept the +following variables: + +- `application`: the Argo CD `Application` resource. Note that all +`Application` resource's fields will be available. +- `project`: the Argo CD `AppProject` resource. Similar to the +`application` variable, all `AppProject` resource's fields will be +available. + +The `.spec.if` field can be used to provide extra custom logic to +decide if a given subject should have their access elevated. The field +will be evaluated using the [expr][5] syntax and the same variables +above will be also available. + +The example below demonstrates how the `AccessBinding` can be +configured: + +```yaml +apiVersion: ephemeral-access.argoproj-labs.io/v1alpha1 +kind: AccessBinding +metadata: + name: some-access-binding +spec: + ordinal: 1 + friendlyName: "Devops (Write)" + subjects: + - group1 + - role-{{.application.metadata.labels.some-label}} + if: "application.metadata.labels.some-label != nil" + roleTemplateRef: + name: devops +``` + +### AccessRequest + +The `AccessRequest` resource is automatically generated by the backend +service based on preconfigured values. This is the main resouce used +to drive how and for how long the user should have their access +elevated. + +The example below demonstrates how the `AccessRequest` can be +configured: + +```yaml +apiVersion: ephemeral-access.argoproj-labs.io/v1alpha1 +kind: AccessRequest +metadata: + name: some-application-username + namespace: ephemeral +spec: + application: + name: ephemeral + namespace: argocd + duration: '1m' + role: + friendlyName: Devops (Write) + ordinal: 1 + templateName: devops + subject: + username: some_user@fakedomain.com +``` + +### RoleTemplate + +The `RoleTemplate` defines a templated Argo CD RBAC policies. Once the +elevated access is requested and approved, the policies will be +rendered and dynamicaly associated with the AppProject related with +the access request. + +The following variable are available to be used in the templated +fields (.spec.description and .spec.policies): + +- `role`: generated string that must be used as the second value in +the policy csv as demonstrated in the example below. +- `project`: the Argo CD AppProject name associated with the access request +- `application`: the Argo CD Application name associated with the +access request +- `namespace`: the namespace where the Argo CD Application lives. + +The example below demonstrates how the `RoleTemplate` can be +configured: + +```yaml +apiVersion: ephemeral-access.argoproj-labs.io/v1alpha1 +kind: RoleTemplate +metadata: + name: devops +spec: + description: write permission in application {{.application}} + name: "devops" + policies: + - p, {{.role}}, applications, sync, {{.project}}/{{.application}}, allow + - p, {{.role}}, applications, action/*, {{.project}}/{{.application}}, allow + - p, {{.role}}, applications, delete/*/Pod/*, {{.project}}/{{.application}}, allow +``` + +## Contributing ### Development -Build the image locally +All necessary development tasks are available as `make targets`. Some +of the targets will make use of external tools. All external tools +used by this project will be automatically downloaded in the `bin` +folder of this repo as required. + +Run the following command to have a full documented list of make +targets available as part of this project: ```bash -IMAGE_NAMESPACE="my.company.com/argoproj-labs" IMAGE_TAG="$(git rev-parse --abbrev-ref HEAD)" make docker-build +make help ``` -Install in kubernetes cluster +#### Building + +For building all component of this project simply run `make` (which is +an alias for `make build`). This target will: +- Build the Go project. The binary will be placed in the `bin` folder + of this repo. +- Build the UI extension. The UI extension will be packaged in the + `ui/extension.tar` file in this repo. + +To build a docker image with custom namespace and tag run ```bash -IMAGE_NAMESPACE="my.company.com/argoproj-labs" IMAGE_TAG="$(git rev-parse --abbrev-ref HEAD)" make deploy-local +IMAGE_NAMESPACE="my.company.com/argoproj-labs" IMAGE_TAG="$(git rev-parse --abbrev-ref HEAD)" make docker-build ``` + +[1]: https://github.com/argoproj-labs/argocd-ephemeral-access/releases +[2]: https://github.com/argoproj-labs/argocd-extension-installer +[3]: https://github.com/argoproj-labs/argocd-ephemeral-access/blob/main/config/backend/config.yaml +[4]: https://github.com/argoproj-labs/argocd-ephemeral-access/blob/main/config/controller/config.yaml +[5]: https://github.com/expr-lang/expr diff --git a/docs/assets/EphemeralAccessExtensionDiagram.png b/docs/assets/EphemeralAccessExtensionDiagram.png new file mode 100644 index 0000000..8637424 Binary files /dev/null and b/docs/assets/EphemeralAccessExtensionDiagram.png differ diff --git a/scripts/manifests-release.sh b/scripts/manifests-release.sh index 76946d9..767df47 100755 --- a/scripts/manifests-release.sh +++ b/scripts/manifests-release.sh @@ -16,9 +16,10 @@ if [ -z "$IMAGE_TAG" ]; then exit 1 fi -IMAGE_QUAY="quay.io/argoprojlabs/argocd-ephemeral-access:$IMAGE_TAG" +IMAGE_NAMESPACE="${IMAGE_NAMESPACE:-quay.io/argoprojlabs/argocd-ephemeral-access}" +IMAGE_FQN="$IMAGE_NAMESPACE:$IMAGE_TAG" $KUSTOMIZE version -cd ${SRCROOT}/config/default && $KUSTOMIZE edit set image argoproj-labs/argocd-ephemeral-access=${IMAGE_QUAY} +cd ${SRCROOT}/config/default && $KUSTOMIZE edit set image argoproj-labs/argocd-ephemeral-access=${IMAGE_FQN} echo "${AUTOGENMSG}" > "${SRCROOT}/install.yaml" $KUSTOMIZE build "${SRCROOT}/config/default" >> "${SRCROOT}/install.yaml"