From bcd34c6e0499c6d00954fb8cb34b6fe6b9a3dcd4 Mon Sep 17 00:00:00 2001 From: Wayne Starr Date: Mon, 23 Sep 2024 18:00:34 -0600 Subject: [PATCH] feat!: allow configurability of SSH and harden application settings (#196) ## Description This PR disables SSH more fully by default but adds an option to reconfigure it later - it also adds a way to harden specific settings in GitLab declaratively. > [!IMPORTANT] > :warning: BREAKING CHANGE - this is a breaking change as it will force hardened settings on the end user unless the settingsJob is disabled or reconfigured. ## Related Issue Fixes #189 Fixes #190 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Other (security config, docs update, etc) ## Checklist before merging - [X] Test, docs, adr added or updated as needed - [X] [Contributor Guide Steps](https://github.com/defenseunicorns/uds-package-gitlab/blob/main/CONTRIBUTING.md#developer-workflow) followed Release-As: v17.2.7-uds.1 --- .github/workflows/commitlint.yaml | 2 +- .github/workflows/lint.yaml | 2 +- .github/workflows/tag-and-release.yaml | 6 +- .github/workflows/test.yaml | 8 +-- .gitignore | 3 + .yamllint | 2 +- bundle/uds-bundle.yaml | 13 ++++ {chart => charts/config}/.helmignore | 0 {chart => charts/config}/Chart.yaml | 0 .../config}/templates/gitlab-sso-secret.yaml | 0 .../postgres-peerauthentication.yaml | 0 .../config}/templates/postgres-secret.yaml | 0 .../templates/redis-peerauthentication.yaml | 0 .../config}/templates/redis-secret.yaml | 0 .../templates/sidekiq-pod-monitor.yaml | 0 charts/config/templates/ssh-gateway.yaml | 17 +++++ .../config/templates/ssh-virtual-service.yaml | 20 ++++++ .../config}/templates/uds-package.yaml | 11 ++++ {chart => charts/config}/values.yaml | 5 ++ charts/settings/.helmignore | 23 +++++++ charts/settings/Chart.yaml | 18 ++++++ charts/settings/templates/_settings-pod.tpl | 43 +++++++++++++ .../templates/service-account-role.yaml | 40 ++++++++++++ .../settings/templates/settings-cron-job.yaml | 17 +++++ charts/settings/templates/settings-job.yaml | 12 ++++ .../settings/templates/settings-secret.yaml | 10 +++ charts/settings/values.yaml | 61 ++++++++++++++++++ common/zarf.yaml | 13 ++-- docs/configuration.md | 63 ++++++++++++++++++- tasks.yaml | 17 +++-- tasks/publish.yaml | 8 +-- tasks/test.yaml | 47 ++++++++------ tests/auth.setup.ts | 8 +++ tests/data/zarf.yaml | 12 ---- tests/gitlab.test.ts | 8 +-- uds-config.yaml | 22 +++++++ values/common-values.yaml | 10 +-- zarf.yaml | 6 ++ 38 files changed, 454 insertions(+), 73 deletions(-) rename {chart => charts/config}/.helmignore (100%) rename {chart => charts/config}/Chart.yaml (100%) rename {chart => charts/config}/templates/gitlab-sso-secret.yaml (100%) rename {chart => charts/config}/templates/postgres-peerauthentication.yaml (100%) rename {chart => charts/config}/templates/postgres-secret.yaml (100%) rename {chart => charts/config}/templates/redis-peerauthentication.yaml (100%) rename {chart => charts/config}/templates/redis-secret.yaml (100%) rename {chart => charts/config}/templates/sidekiq-pod-monitor.yaml (100%) create mode 100644 charts/config/templates/ssh-gateway.yaml create mode 100644 charts/config/templates/ssh-virtual-service.yaml rename {chart => charts/config}/templates/uds-package.yaml (98%) rename {chart => charts/config}/values.yaml (97%) create mode 100644 charts/settings/.helmignore create mode 100644 charts/settings/Chart.yaml create mode 100644 charts/settings/templates/_settings-pod.tpl create mode 100644 charts/settings/templates/service-account-role.yaml create mode 100644 charts/settings/templates/settings-cron-job.yaml create mode 100644 charts/settings/templates/settings-job.yaml create mode 100644 charts/settings/templates/settings-secret.yaml create mode 100644 charts/settings/values.yaml delete mode 100644 tests/data/zarf.yaml create mode 100644 uds-config.yaml diff --git a/.github/workflows/commitlint.yaml b/.github/workflows/commitlint.yaml index e0b29776..0e029f96 100644 --- a/.github/workflows/commitlint.yaml +++ b/.github/workflows/commitlint.yaml @@ -12,4 +12,4 @@ on: jobs: validate: name: Validate - uses: defenseunicorns/uds-common/.github/workflows/commitlint.yaml@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/workflows/commitlint.yaml@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a9cfd7b3..3b780690 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: Environment setup - uses: defenseunicorns/uds-common/.github/actions/setup@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/setup@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: registry1Username: ${{ secrets.IRON_BANK_ROBOT_USERNAME }} registry1Password: ${{ secrets.IRON_BANK_ROBOT_PASSWORD }} diff --git a/.github/workflows/tag-and-release.yaml b/.github/workflows/tag-and-release.yaml index 0a399466..db91a34f 100644 --- a/.github/workflows/tag-and-release.yaml +++ b/.github/workflows/tag-and-release.yaml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Environment setup - uses: defenseunicorns/uds-common/.github/actions/setup@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/setup@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: registry1Username: ${{ secrets.IRON_BANK_ROBOT_USERNAME }} registry1Password: ${{ secrets.IRON_BANK_ROBOT_PASSWORD }} @@ -67,10 +67,10 @@ jobs: - name: Debug Output if: ${{ always() }} - uses: defenseunicorns/uds-common/.github/actions/debug-output@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/debug-output@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 - name: Save logs if: always() - uses: defenseunicorns/uds-common/.github/actions/save-logs@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/save-logs@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: suffix: '${{ matrix.flavor }}-${{ matrix.architecture }}-${{ github.run_id }}-${{ github.run_attempt }}' diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fb70a764..0df612ae 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -46,25 +46,25 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Environment setup - uses: defenseunicorns/uds-common/.github/actions/setup@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/setup@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: registry1Username: ${{ secrets.IRON_BANK_ROBOT_USERNAME }} registry1Password: ${{ secrets.IRON_BANK_ROBOT_PASSWORD }} ghToken: ${{ secrets.GITHUB_TOKEN }} - name: Test - uses: defenseunicorns/uds-common/.github/actions/test@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/test-deploy@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: flavor: ${{ matrix.flavor }} type: ${{ matrix.type }} - name: Debug Output if: ${{ always() }} - uses: defenseunicorns/uds-common/.github/actions/debug-output@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/debug-output@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 - name: Save logs if: always() - uses: defenseunicorns/uds-common/.github/actions/save-logs@76287d41ec5f06ecbdd0a6453877a78675aceffe # v0.11.2 + uses: defenseunicorns/uds-common/.github/actions/save-logs@e3008473beab00b12a94f9fcc7340124338d5c08 # v0.13.1 with: suffix: ${{ matrix.type }}-${{ matrix.flavor }}-${{ github.run_id }}-${{ github.run_attempt }} diff --git a/.gitignore b/.gitignore index 58f13316..bf31a183 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ overlay-values-* # Tests node_modules/ .playwright/ +tests/data/gitlab-test-ssh-key +tests/data/gitlab-test-ssh-key.pub +tests/data/uds-package-test-* diff --git a/.yamllint b/.yamllint index 61ce3120..a7729d87 100644 --- a/.yamllint +++ b/.yamllint @@ -3,7 +3,7 @@ yaml-files: - '.yamllint' ignore: - - '**/chart/templates**' + - '**/charts/**/templates**' rules: anchors: enable diff --git a/bundle/uds-bundle.yaml b/bundle/uds-bundle.yaml index bd0b800a..5e356212 100644 --- a/bundle/uds-bundle.yaml +++ b/bundle/uds-bundle.yaml @@ -75,6 +75,11 @@ packages: overrides: gitlab: uds-gitlab-config: + values: + - path: ssh.enabled + value: true + - path: ssh.port + value: 2223 variables: - name: GITLAB_SSO_ENABLED description: "Boolean to enable or disable sso things" @@ -90,6 +95,10 @@ packages: path: "sso.requiredGroups" gitlab: values: + - path: gitlab.gitlab-shell.enabled + value: true + - path: global.shell.port + value: 2223 - path: global.psql.host value: pg-cluster.postgres.svc.cluster.local - path: "global.psql.username" @@ -132,3 +141,7 @@ packages: - name: SHELL_REPLICAS description: "Gitlab Shell Min Replicas" path: "gitlab.gitlab-shell.minReplicas" + uds-gitlab-settings: + values: + - path: settingsJob.application.enabled_git_access_protocol + value: all diff --git a/chart/.helmignore b/charts/config/.helmignore similarity index 100% rename from chart/.helmignore rename to charts/config/.helmignore diff --git a/chart/Chart.yaml b/charts/config/Chart.yaml similarity index 100% rename from chart/Chart.yaml rename to charts/config/Chart.yaml diff --git a/chart/templates/gitlab-sso-secret.yaml b/charts/config/templates/gitlab-sso-secret.yaml similarity index 100% rename from chart/templates/gitlab-sso-secret.yaml rename to charts/config/templates/gitlab-sso-secret.yaml diff --git a/chart/templates/postgres-peerauthentication.yaml b/charts/config/templates/postgres-peerauthentication.yaml similarity index 100% rename from chart/templates/postgres-peerauthentication.yaml rename to charts/config/templates/postgres-peerauthentication.yaml diff --git a/chart/templates/postgres-secret.yaml b/charts/config/templates/postgres-secret.yaml similarity index 100% rename from chart/templates/postgres-secret.yaml rename to charts/config/templates/postgres-secret.yaml diff --git a/chart/templates/redis-peerauthentication.yaml b/charts/config/templates/redis-peerauthentication.yaml similarity index 100% rename from chart/templates/redis-peerauthentication.yaml rename to charts/config/templates/redis-peerauthentication.yaml diff --git a/chart/templates/redis-secret.yaml b/charts/config/templates/redis-secret.yaml similarity index 100% rename from chart/templates/redis-secret.yaml rename to charts/config/templates/redis-secret.yaml diff --git a/chart/templates/sidekiq-pod-monitor.yaml b/charts/config/templates/sidekiq-pod-monitor.yaml similarity index 100% rename from chart/templates/sidekiq-pod-monitor.yaml rename to charts/config/templates/sidekiq-pod-monitor.yaml diff --git a/charts/config/templates/ssh-gateway.yaml b/charts/config/templates/ssh-gateway.yaml new file mode 100644 index 00000000..81d45042 --- /dev/null +++ b/charts/config/templates/ssh-gateway.yaml @@ -0,0 +1,17 @@ +{{- if .Values.ssh.enabled }} +apiVersion: networking.istio.io/v1beta1 +kind: Gateway +metadata: + name: gitlab-ssh-gateway + namespace: istio-tenant-gateway +spec: + selector: + app: tenant-ingressgateway + servers: + - hosts: + - gitlab.{{ .Values.domain }} + port: + name: tcp-ssh + number: {{ .Values.ssh.port }} + protocol: TCP +{{- end }} diff --git a/charts/config/templates/ssh-virtual-service.yaml b/charts/config/templates/ssh-virtual-service.yaml new file mode 100644 index 00000000..b8676a72 --- /dev/null +++ b/charts/config/templates/ssh-virtual-service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.ssh.enabled }} +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: gitlab-ssh + namespace: {{ .Release.Namespace }} +spec: + gateways: + - istio-tenant-gateway/gitlab-ssh-gateway + hosts: + - gitlab.{{ .Values.domain }} + tcp: + - match: + - port: {{ .Values.ssh.port }} + route: + - destination: + host: gitlab-gitlab-shell.gitlab.svc.cluster.local + port: + number: {{ .Values.ssh.port }} +{{- end }} diff --git a/chart/templates/uds-package.yaml b/charts/config/templates/uds-package.yaml similarity index 98% rename from chart/templates/uds-package.yaml rename to charts/config/templates/uds-package.yaml index f9ae3fa4..4ca1d5c8 100644 --- a/chart/templates/uds-package.yaml +++ b/charts/config/templates/uds-package.yaml @@ -103,6 +103,17 @@ spec: - direction: Ingress remoteGenerated: IntraNamespace + {{- if .Values.ssh.enabled }} + - direction: Ingress + selector: + app: gitlab-shell + remoteNamespace: istio-tenant-gateway + remoteSelector: + app: tenant-ingressgateway + port: 2222 + description: "SSH Ingress" + {{- end }} + # ingress from runner only if runner lives in cluster. Otherwise, it goes through the gateway {{- if .Values.runner.internal }} - direction: Ingress diff --git a/chart/values.yaml b/charts/config/values.yaml similarity index 97% rename from chart/values.yaml rename to charts/config/values.yaml index 2a4ca338..6641154f 100644 --- a/chart/values.yaml +++ b/charts/config/values.yaml @@ -1,4 +1,9 @@ domain: "###ZARF_VAR_DOMAIN###" + +ssh: + enabled: false + port: 2222 + sso: enabled: true protocol: saml diff --git a/charts/settings/.helmignore b/charts/settings/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/settings/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/settings/Chart.yaml b/charts/settings/Chart.yaml new file mode 100644 index 00000000..3aafc840 --- /dev/null +++ b/charts/settings/Chart.yaml @@ -0,0 +1,18 @@ +apiVersion: v2 +name: uds-gitlab-settings +description: uds-gitlab-settings + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 diff --git a/charts/settings/templates/_settings-pod.tpl b/charts/settings/templates/_settings-pod.tpl new file mode 100644 index 00000000..9eab7cbc --- /dev/null +++ b/charts/settings/templates/_settings-pod.tpl @@ -0,0 +1,43 @@ +# Reusable Pod spec for settings jobs +{{- define "uds-gitlab-settings.settings-pod" }} +metadata: + labels: + app: gitlab +spec: + serviceAccountName: gitlab-settings-sa + containers: + - name: gitlab-settings + image: "{{ .Values.global.kubectl.image.repository }}:{{ .Values.global.kubectl.image.tag }}" + command: ["/bin/sh", "-c"] + args: + - | + # Read the JSON file from the mounted application settings secret + SETTINGS=$(cat /etc/gitlab-settings/application.json) + + # Dynamically parse each key-value pair in the JSON using yq and construct query parameters + QUERY_PARAMS=$(echo $SETTINGS | yq e 'to_entries | map("\(.key)=\(.value)") | join("&")' -) + + # Generate and capture a GitLab token from the GitLab Toolbox Rails Console + TOKEN=$(kubectl exec -n gitlab deployment/gitlab-toolbox -- \ + gitlab-rails runner -e production \ + "random_token = SecureRandom.hex(32); token = User.find_by_username('root').personal_access_tokens.create(scopes: ['api', 'admin_mode'], name: 'Application Settings Token', expires_at: 1.days.from_now); token.set_token(random_token); token.save!; puts random_token" | tail -n 1) + + # Use the generated token to set GitLab settings + kubectl exec -n gitlab deployment/gitlab-toolbox -- \ + curl --request PUT --header "PRIVATE-TOKEN: $TOKEN" \ + "http://gitlab-webservice-default.gitlab.svc.cluster.local:8181/api/v4/application/settings?$QUERY_PARAMS" + + # Revoke the token after use + kubectl exec -n gitlab deployment/gitlab-toolbox -- \ + gitlab-rails runner -e production \ + "token = PersonalAccessToken.find_by_token('$TOKEN'); token.revoke!" + volumeMounts: + - name: gitlab-settings-volume + mountPath: /etc/gitlab-settings + readOnly: true + restartPolicy: OnFailure + volumes: + - name: gitlab-settings-volume + secret: + secretName: gitlab-settings-secret +{{- end }} diff --git a/charts/settings/templates/service-account-role.yaml b/charts/settings/templates/service-account-role.yaml new file mode 100644 index 00000000..51cfe1a6 --- /dev/null +++ b/charts/settings/templates/service-account-role.yaml @@ -0,0 +1,40 @@ +{{- if .Values.settingsJob.enabled }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: gitlab-settings-sa + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: gitlab-settings-role + namespace: {{ .Release.Namespace }} +rules: + # Only allow exec into the toolbox pod + - apiGroups: [""] + resources: ["pods/exec"] + verbs: ["create"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["list"] + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get"] + resourceNames: + - gitlab-toolbox +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: gitlab-settings-rolebinding + namespace: {{ .Release.Namespace }} +subjects: + - kind: ServiceAccount + name: gitlab-settings-sa + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: gitlab-settings-role + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/settings/templates/settings-cron-job.yaml b/charts/settings/templates/settings-cron-job.yaml new file mode 100644 index 00000000..c08d424a --- /dev/null +++ b/charts/settings/templates/settings-cron-job.yaml @@ -0,0 +1,17 @@ +{{- if .Values.settingsJob.enabled }} +# CronJob to reapply settings on schedule +apiVersion: batch/v1 +kind: CronJob +metadata: + name: gitlab-settings-cronjob + namespace: {{ .Release.Namespace }} +spec: + schedule: "{{ .Values.settingsJob.schedule }}" + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + jobTemplate: + spec: + ttlSecondsAfterFinished: 30 + template: + {{ include "uds-gitlab-settings.settings-pod" . | indent 8 }} +{{- end }} diff --git a/charts/settings/templates/settings-job.yaml b/charts/settings/templates/settings-job.yaml new file mode 100644 index 00000000..5a82737d --- /dev/null +++ b/charts/settings/templates/settings-job.yaml @@ -0,0 +1,12 @@ +{{- if .Values.settingsJob.enabled }} +# Job to apply settings immediately on deployment +apiVersion: batch/v1 +kind: Job +metadata: + name: gitlab-settings-job + namespace: {{ .Release.Namespace }} +spec: + ttlSecondsAfterFinished: 30 + template: + {{ include "uds-gitlab-settings.settings-pod" . | indent 4 }} +{{- end }} diff --git a/charts/settings/templates/settings-secret.yaml b/charts/settings/templates/settings-secret.yaml new file mode 100644 index 00000000..061ce34b --- /dev/null +++ b/charts/settings/templates/settings-secret.yaml @@ -0,0 +1,10 @@ +{{- if .Values.settingsJob.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: gitlab-settings-secret + namespace: gitlab +type: Opaque +stringData: + application.json: {{ .Values.settingsJob.application | toJson | quote }} +{{- end }} diff --git a/charts/settings/values.yaml b/charts/settings/values.yaml new file mode 100644 index 00000000..4add9651 --- /dev/null +++ b/charts/settings/values.yaml @@ -0,0 +1,61 @@ +global: + kubectl: + image: + repository: registry.gitlab.com/gitlab-org/build/cng/kubectl + tag: v17.2.4 + +settingsJob: + enabled: true + schedule: "0 2 * * *" # Run at 2:00 AM every day + # Settings here come from: https://docs.gitlab.com/ee/api/settings.html + # NOTE: These keys are converted to a JSON object that is converted to query + # parameters later on so any nested objects/arrays should be encoded as a + # string query parameter instead of their full type. + application: + default_snippet_visibility: private + default_project_visibility: private + default_group_visibility: private + # restricted_visibility_levels is actually an array but is a single element string to work here + restricted_visibility_levels: public + enabled_git_access_protocol: http + rsa_key_restriction: 2048 + ecdsa_key_restriction: 256 + ecdsa_sk_key_restriction: 256 + ed25519_key_restriction: -1 + ed25519_sk_key_restriction: -1 + dsa_key_restriction: -1 + disable_feed_token: true + gravatar_enabled: false + signup_enabled: false + email_confirmation_setting: hard + password_authentication_enabled_for_web: false + password_authentication_enabled_for_git: false + minimum_password_length: 16 + password_number_required: true + password_symbol_required: true + password_uppercase_required: true + password_lowercase_required: true + admin_mode: true + notify_on_unknown_sign_in: true + dns_rebinding_protection_enabled: true + throttle_authenticated_api_enabled: true + throttle_authenticated_api_period_in_seconds: 3600 + throttle_authenticated_api_requests_per_period: 7200 + throttle_authenticated_packages_api_enabled: true + throttle_authenticated_packages_api_period_in_seconds: 3600 + throttle_authenticated_packages_api_requests_per_period: 7200 + throttle_authenticated_web_enabled: true + throttle_authenticated_web_period_in_seconds: 3600 + throttle_authenticated_web_requests_per_period: 7200 + throttle_unauthenticated_api_enabled: true + throttle_unauthenticated_api_period_in_seconds: 3600 + throttle_unauthenticated_api_requests_per_period: 3600 + throttle_unauthenticated_packages_api_enabled: true + throttle_unauthenticated_packages_api_period_in_seconds: 3600 + throttle_unauthenticated_packages_api_requests_per_period: 3600 + throttle_unauthenticated_web_enabled: true + throttle_unauthenticated_web_period_in_seconds: 3600 + throttle_unauthenticated_web_requests_per_period: 3600 + usage_ping_enabled: false + include_optional_metrics_in_service_ping: false + version_check_enabled: false diff --git a/common/zarf.yaml b/common/zarf.yaml index b3605acb..b2e4be04 100644 --- a/common/zarf.yaml +++ b/common/zarf.yaml @@ -11,7 +11,7 @@ components: - name: uds-gitlab-config namespace: gitlab version: 0.2.0 - localPath: ../chart + localPath: ../charts/config - name: gitlab namespace: gitlab url: https://charts.gitlab.io/ @@ -19,6 +19,10 @@ components: version: "8.3.3" valuesFiles: - ../values/common-values.yaml + - name: uds-gitlab-settings + namespace: gitlab + version: 0.1.0 + localPath: ../charts/settings actions: onDeploy: after: @@ -44,13 +48,6 @@ components: name: gitlab-registry namespace: gitlab condition: Available - - description: GitLab Shell to be Healthy - wait: - cluster: - kind: Deployment - name: gitlab-gitlab-shell - namespace: gitlab - condition: Available - description: GitLab Toolbox to be Healthy wait: cluster: diff --git a/docs/configuration.md b/docs/configuration.md index 8aeb76e1..45ddbe3f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -51,7 +51,7 @@ https://github.com/defenseunicorns/uds-software-factory/issues/45 GitLab uses Redis as a key value store for caching, job queueing and more and supports external providers (such as Elasticache) as well as the [UDS Valkey](https://github.com/defenseunicorns/uds-package-valkey/) package to provide the service. -### Manual Database Connection +### Manual Keystore Connection You can use the following Helm overrides to configure a connection to Redis / Valkey: @@ -72,3 +72,64 @@ You can use the following Helm overrides to configure a connection to Redis / Va - `global.redis.scheme` - provides the scheme to use to connect to the key value store (i.e. `redis` or `rediss`) - `global.redis.host` - provides the endpoint to use to connect to the key value store (i.e. `pg-cluster.postgres.svc.cluster.local`) - `global.redis.port` - provides the port to use to connect to the key value store (defaults to `6379`) + +## Configuring SSH + +By default this package deploys GitLab in an HTTPS-only mode - this reduces the attack surface by removing one potential point of ingress, but if you need to enable SSH git cloning and have mitigated this risk in other ways you can do so with the following overrides: + +#### `uds-core` package: + +Before configuring the GitLab package to allow SSH traffic you will need to also allow the traffic through UDS Core (and everything before it). To configure UDS Core to pass through SSH you will need to add the following (where `2222` is replaced with your chosen SSH port): + +```yaml + overrides: + istio-tenant-gateway: + gateway: + values: + - path: "service.ports" + value: + - name: status-port + port: 15021 + protocol: TCP + targetPort: 15021 + - name: http2 + port: 80 + protocol: TCP + targetPort: 80 + - name: https + port: 443 + protocol: TCP + targetPort: 443 + - name: tcp-ssh + port: 2222 + protocol: TCP + targetPort: 2222 +``` + +This will allow SSH traffic to traverse the LoadBalancer and hit the Istio Gateway that is configured in the GitLab chart. + +#### `uds-gitlab-config` chart: + +- `ssh.enabled` - set this to `true` to enable the additional gateway and virtual service +- `ssh.port` - set this to a different integer if you'd like to expose ssh over a different port (defaults to `2222`) + +#### `gitlab` chart: + +- `gitlab.gitlab-shell.enabled` - set this to `true` to enable the SSH daemon within the GitLab deployment +- `global.shell.port` - set this if you overrode `ssh.port` above to correct the port for the `gitlab-shell` service and the UI (defaults to `2222`) + +#### `uds-gitlab-settings` chart: + +- `settingsJob.application.enabled_git_access_protocol` - set this to `all` to reenable the SSH option when selecting a repository's clone dropdown + +## Configuring GitLab Settings + +This package contains an additional chart that will force GitLab application settings to take the values recommended in the [GitLab Application Hardening Recommendations](https://docs.gitlab.com/ee/security/hardening_application_recommendations.html) guide. These settings may need to be modified for your instance or you may wish to make tweaks to add additional settings that can be found in the [GitLab Application Settings documentation](https://docs.gitlab.com/ee/api/settings.html). + +It is recommended to inspect these settings and further lock them down for your specific environment if you are able. You can change or add settings by adding your desired key to the `settingsJob.application` value of the `uds-gitlab-settings` chart. This YAML object will be converted to a JSON object and then into query parameters to pass to the [GitLab Application Settings API](https://docs.gitlab.com/ee/api/settings.html). + +> [!IMPORTANT] +> Simple key-value pairs can be set as-is, however objects/arrays should be set to the values that would be expected as a query parameter. As an example, `{"restricted_visibility_levels": ["public"]}` becomes `restricted_visibility_levels: public` in the YAML object. + +> [!TIP] +> If you wish to disable the settings Job and CronJob and keep GitLab's default application settings you can do so with the `settingsJob.enabled` value. You can also adjust the CronJob schedule (when it will reset the application settings) with the `settingsJob.schedule` value. diff --git a/tasks.yaml b/tasks.yaml index 5ef90e5d..e5287b9f 100644 --- a/tasks.yaml +++ b/tasks.yaml @@ -2,11 +2,12 @@ includes: - cleanup: ./tasks/cleanup.yaml - dependencies: ./tasks/dependencies.yaml - test: ./tasks/test.yaml - - create: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/create.yaml - - lint: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/lint.yaml - - pull: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/pull.yaml - - deploy: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/deploy.yaml - - setup: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/setup.yaml + - create: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/create.yaml + - lint: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/lint.yaml + - pull: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/pull.yaml + - deploy: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/deploy.yaml + - setup: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/setup.yaml + - upgrade: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/upgrade.yaml tasks: - name: default @@ -33,11 +34,9 @@ tasks: - name: create-gl-latest-release-bundle description: Create UDS Gitlab bundle with dependencies based on the latest release actions: - - task: pull:latest-package-release + - task: upgrade:create-latest-tag-bundle with: - spoof_release: "true" - - task: dependencies:create - - task: create:test-bundle + dep_commands: uds run dependencies:create - name: dev description: deploy against running cluster diff --git a/tasks/publish.yaml b/tasks/publish.yaml index 95341956..4165c795 100644 --- a/tasks/publish.yaml +++ b/tasks/publish.yaml @@ -1,10 +1,10 @@ includes: - dependencies: ./dependencies.yaml - test: ./test.yaml - - create: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/create.yaml - - deploy: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/deploy.yaml - - publish: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/publish.yaml - - setup: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.11.2/tasks/setup.yaml + - create: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/create.yaml + - deploy: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/deploy.yaml + - publish: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/publish.yaml + - setup: https://raw.githubusercontent.com/defenseunicorns/uds-common/v0.13.1/tasks/setup.yaml tasks: - name: build-package diff --git a/tasks/test.yaml b/tasks/test.yaml index 38d8f37d..0589ea3a 100644 --- a/tasks/test.yaml +++ b/tasks/test.yaml @@ -7,7 +7,6 @@ tasks: actions: - task: test:ingress - task: test:ui - - task: test:git - name: ingress actions: @@ -24,32 +23,42 @@ tasks: exit 1 fi - - name: git - description: GitLab Repository Mirror Checks - actions: - - cmd: | - ./uds zarf package create --confirm - dir: tests/data - - task: create-doug-pat - - cmd: | - ./uds zarf package mirror-resources \ - zarf-package-gitlab-git-tests-${UDS_ARCH}-0.0.1.tar.zst \ - --git-url "https://gitlab.uds.dev" \ - --git-push-username "doug" \ - --git-push-password "${GITLAB_TOKEN}" \ - --confirm - dir: tests/data - - name: ui - description: GitLab UI Checks + description: GitLab UI/Auth Checks actions: + # Setup the project name and SSH Key + - cmd: echo "uds-package-test-$(date +%s)" + setVariables: + - name: PROJECT_NAME + - cmd: rm -f ./gitlab-test-ssh-key && ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ./gitlab-test-ssh-key -P "" + dir: tests/data + # Login, and create a project - cmd: | - docker run --rm --ipc=host --net=host --mount type=bind,source="$(pwd)",target=/app mcr.microsoft.com/playwright:v1.43.1-jammy sh -c " \ + docker run --rm --ipc=host --net=host -e SSH_PUBLIC_KEY="$(cat ./data/gitlab-test-ssh-key.pub)" -e PROJECT_NAME="${PROJECT_NAME}" --mount type=bind,source="$(pwd)",target=/app mcr.microsoft.com/playwright:v1.43.1-jammy sh -c " \ cd app && \ npm ci && \ npx playwright test \ " dir: tests + # Pull the project via SSH (gateway -> gitlab-shell -> gitaly) + - cmd: | + mkdir -p "${PROJECT_NAME}" && cd "${PROJECT_NAME}" && git init + git config core.sshCommand "ssh -i ../gitlab-test-ssh-key -o StrictHostKeyChecking=no" + git remote add origin "ssh://git@gitlab.uds.dev:2223/doug/${PROJECT_NAME}-firefox.git" + git pull origin main + dir: tests/data + # Push to the project via HTTPS (gateway -> gitlab-webservice -> gitaly) + - task: create-doug-pat + - cmd: | + cd "${PROJECT_NAME}" + git config commit.gpgsign false + git remote set-url origin "https://doug:${GITLAB_TOKEN}@gitlab.uds.dev/doug/${PROJECT_NAME}-firefox.git" + touch hello-kitteh.txt && git add . && git commit -m "Add hello kitteh" + git push -u origin --all + dir: tests/data + # Cleanup the git repo + - cmd: rm -rf "${PROJECT_NAME}" + dir: tests/data - name: create-doug-admin description: Create "doug" account as admin (must be run *before* first login) diff --git a/tests/auth.setup.ts b/tests/auth.setup.ts index d8acb037..b678405a 100644 --- a/tests/auth.setup.ts +++ b/tests/auth.setup.ts @@ -23,4 +23,12 @@ setup('authenticate', async ({ page, context }) => { await page.context().storageState({ path: authFile }); await expect(page).toHaveURL('/dashboard/projects'); + + await page.goto("/-/user_settings/ssh_keys") + await page.getByRole('button', { name: "Add new key" }).click(); + + await page.getByLabel('Key').fill(process.env.SSH_PUBLIC_KEY!); + await page.getByRole('button', { name: "Add key" }).click(); + + await page.goto('/dashboard/projects'); }) diff --git a/tests/data/zarf.yaml b/tests/data/zarf.yaml deleted file mode 100644 index 1f798886..00000000 --- a/tests/data/zarf.yaml +++ /dev/null @@ -1,12 +0,0 @@ -kind: ZarfPackageConfig -metadata: - name: gitlab-git-tests - version: 0.0.1 - description: A package with git repos used for testing - -components: - - name: git-repos - repos: - # This references a commit that has a .gitlab-ci.yml in it - to update this push a PR and a new commit. - - https://github.com/defenseunicorns/uds-package-gitlab-runner.git - - https://github.com/defenseunicorns/uds-core.git diff --git a/tests/gitlab.test.ts b/tests/gitlab.test.ts index 88e61ab8..fb71653c 100644 --- a/tests/gitlab.test.ts +++ b/tests/gitlab.test.ts @@ -1,12 +1,8 @@ import { test, expect } from "@playwright/test"; import path from "path"; -function randomProjectName(prefix: string = 'uds-package-test') { - return [ prefix, Math.floor((Math.random() * 10_000)) ].join('-'); -} - -test('setup a project', async ({ page }) => { - const projectName = randomProjectName(); +test('setup a project', async ({ page, browserName }) => { + const projectName = `${process.env.PROJECT_NAME!}-${browserName}` await page.goto('/projects/new#blank_project'); await page.getByLabel('Project name').fill(projectName); diff --git a/uds-config.yaml b/uds-config.yaml new file mode 100644 index 00000000..081d221e --- /dev/null +++ b/uds-config.yaml @@ -0,0 +1,22 @@ +variables: + uds-k3d-dev: + K3D_EXTRA_ARGS: "-p 2223:2223@server:*" + NGINX_EXTRA_PORTS: "[2223]" + core-slim-dev: + TENANT_SERVICE_PORTS: + - name: status-port + port: 15021 + protocol: TCP + targetPort: 15021 + - name: http2 + port: 80 + protocol: TCP + targetPort: 80 + - name: https + port: 443 + protocol: TCP + targetPort: 443 + - name: tcp-ssh + port: 2223 + protocol: TCP + targetPort: 2223 diff --git a/values/common-values.yaml b/values/common-values.yaml index 38d6e1f3..16b189c8 100644 --- a/values/common-values.yaml +++ b/values/common-values.yaml @@ -17,6 +17,7 @@ global: name: registry.###ZARF_VAR_DOMAIN### pages: name: pages.###ZARF_VAR_DOMAIN### + psql: host: ###ZARF_VAR_GITLAB_DB_ENDPOINT### port: 5432 @@ -41,6 +42,9 @@ global: credentials: {} + shell: + port: 2222 + appConfig: object_store: enabled: true @@ -81,8 +85,6 @@ global: providers: - secret: gitlab-sso key: providers - initialDefaults: - signupEnabled: ###ZARF_VAR_GITLAB_SIGNUP_ENABLED### registry: bucket: ###ZARF_VAR_BUCKET_PREFIX###gitlab-registry###ZARF_VAR_BUCKET_SUFFIX### @@ -171,9 +173,9 @@ gitlab: serviceMonitor: enabled: true - # override to enable ssh gitlab-shell: - enabled: true + # override to enable ssh + enabled: false sshDaemon: gitlab-sshd metrics: enabled: true diff --git a/zarf.yaml b/zarf.yaml index d79c8a94..70662d98 100644 --- a/zarf.yaml +++ b/zarf.yaml @@ -46,6 +46,9 @@ components: - name: gitlab valuesFiles: - values/registry1-values.yaml + - name: uds-gitlab-settings + valuesFiles: + - values/registry1-values.yaml images: - "registry1.dso.mil/ironbank/gitlab/gitlab/certificates:17.2.7" - "registry1.dso.mil/ironbank/gitlab/gitlab/cfssl-self-sign:1.6.1" @@ -73,6 +76,9 @@ components: - name: gitlab valuesFiles: - values/upstream-values.yaml + - name: uds-gitlab-settings + valuesFiles: + - values/upstream-values.yaml images: - "registry.gitlab.com/gitlab-org/build/cng/certificates:v17.2.7" - "registry.gitlab.com/gitlab-org/build/cng/cfssl-self-sign:v17.2.7"