From 63306be81011eeec8a9d2a0ec996e990463685bd Mon Sep 17 00:00:00 2001 From: Nicholas Dille Date: Wed, 10 Apr 2024 17:24:18 +0200 Subject: [PATCH] Fixes after 20240410 --- 150_gitlab/010_projects/slides.md | 2 +- 150_gitlab/030_authentication/slides.md | 24 +++++------ 150_gitlab/060_api/slides.md | 2 +- 150_gitlab/090_maintenance/slides.md | 12 +++--- 150_gitlab/100_reverse_proxy/compose.yml | 7 +-- 150_gitlab/100_reverse_proxy/slides.md | 10 ++++- 150_gitlab/135_integrations/compose.yml | 10 ++--- 150_gitlab/140_troubleshooting/slides.md | 12 ++++-- 150_gitlab/150_update/slides.md | 2 +- 150_gitlab/160_runner/slides.md | 14 +++--- 150_gitlab/170_monitoring/compose.yml | 2 +- 150_gitlab/170_monitoring/slides.md | 2 +- 150_gitlab/180_components/compose.yml | 8 ++-- 150_gitlab/190_database/slides.md | 24 +++++++++++ 2024-04-10_heise-GitLab-Ops.html | 52 ++++++++++++++++++++++- 2024-04-10_heise-GitLab-Ops.md | 4 ++ images/logos/heise-academy.png | Bin 0 -> 5326 bytes images/logos/heise-academy.svg | 1 + 18 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 2024-04-10_heise-GitLab-Ops.md create mode 100755 images/logos/heise-academy.png create mode 100755 images/logos/heise-academy.svg diff --git a/150_gitlab/010_projects/slides.md b/150_gitlab/010_projects/slides.md index ba37d99f..e534720b 100644 --- a/150_gitlab/010_projects/slides.md +++ b/150_gitlab/010_projects/slides.md @@ -92,7 +92,7 @@ For example: repository, issues etc. ### Members -Invite other users +Invite users/groups Control role diff --git a/150_gitlab/030_authentication/slides.md b/150_gitlab/030_authentication/slides.md index fbdad962..7666befd 100644 --- a/150_gitlab/030_authentication/slides.md +++ b/150_gitlab/030_authentication/slides.md @@ -83,7 +83,7 @@ User can pull but not push Deploy keys belong to a user who can be blocked [gitlab-org/gitlab#35779](https://gitlab.com/gitlab-org/gitlab/-/issues/35779) -Find and fix deploy keys using Ruby code in rals console [](https://docs.gitlab.com/ee/user/project/deploy_keys/#identify-deploy-keys-associated-with-non-member-and-blocked-users) +Find and fix deploy keys using Ruby code in rails console [](https://docs.gitlab.com/ee/user/project/deploy_keys/#identify-deploy-keys-associated-with-non-member-and-blocked-users) ```ruby DeployKeysProject.with_write_access.find_each do |deploy_key_mapping| @@ -107,7 +107,7 @@ end ## Comparison -| | Password | Personal Access Token | Personal SSH Key | Group Access Token | Group Deploy Token | Project Access Token | Project Deploy Token | Project SSH Key (0) | +| | Password | Personal Access Token | Personal SSH Key | Group Access Token | Group Deploy Token | Project Access Token | Project Deploy Token | Project SSH Key | |-|-|-|-|-|-|-|-|-| | Access to Web UI | Yes | No | No | No | No | No | No | No | | Access to API | Indirect (1) | Yes | No | Yes (2) | No | Yes (3) | No | No | @@ -115,22 +115,18 @@ end | Write git repository | Yes | Yes | Yes | Yes | No | Yes | No | No | | Access CI variables | Yes | Yes (4) | No | Yes (4) | No | Yes (4) | No | No | | Access scope | User | User | User | Group | Group | Project | Project | Project | +| Employee layoffs | Yes | Yes | Yes | No | No | No | No | Yes | | Credential reuse (5) | Possible | No | Possible | No | No | No | No | Possible | | Impact of security incident | High | High | High | Medium | Medium | Low | Low | Medium | | Recommendation | No | No | No | Limited (6) | Limited (6) | Yes | Yes | Limited (6) | -(0) XXX +- (1) Username and password can be used to retrieve a personal access token +- (2) Group only +- (3) Project only +- (4) API only +- (5) Can be used for multiple accounts and on multiple systems +- (6) Acceptable for automation to avoid many project credentials -(1) Username and password can be used to retrieve a personal access token - -(2) Group only - -(3) Project only - -(4) API only - -(5) Can be used for multiple accounts and on multiple systems - -(6) Acceptable for automation to avoid many project credentials + diff --git a/150_gitlab/060_api/slides.md b/150_gitlab/060_api/slides.md index c41e87b8..277bab85 100644 --- a/150_gitlab/060_api/slides.md +++ b/150_gitlab/060_api/slides.md @@ -142,4 +142,4 @@ Rotation API introduced in GitLab 16.0 (May 2023) Automatic reuse detection [](https://docs.gitlab.com/ee/api/personal_access_tokens.html#automatic-reuse-detection) prevents use of rotated tokens: -- Use of old tokens result in revocation of latest token +- Use of old tokens for rotation results in revocation of whole token family diff --git a/150_gitlab/090_maintenance/slides.md b/150_gitlab/090_maintenance/slides.md index da770a8d..39e4607c 100644 --- a/150_gitlab/090_maintenance/slides.md +++ b/150_gitlab/090_maintenance/slides.md @@ -28,9 +28,11 @@ Switch GitLab into read-only mode [ -Configure under Menu Admin Settings Repository Repository Maintenance +Automatically optimize git repositories [](https://docs.gitlab.com/ee/administration/housekeeping.html), especially... +- Compress objects and revisions +- Remove unreachable objects -Regular execution of... -- `git fsck` -- `git repack` (incremental and full) -- `git gc` +Configure under Menu Admin Settings Repository Repository Maintenance +- Enable repository checks (default) +- Enable housekeeping (default) +- Configure optimization period (default: 10 pushes) diff --git a/150_gitlab/100_reverse_proxy/compose.yml b/150_gitlab/100_reverse_proxy/compose.yml index 7a2a7272..3f1a75f2 100644 --- a/150_gitlab/100_reverse_proxy/compose.yml +++ b/150_gitlab/100_reverse_proxy/compose.yml @@ -60,11 +60,12 @@ services: gitlab_pages['enable'] = true pages_external_url 'http://gitlab.${DOMAIN:?You must supply DOMAIN}' - # Required due to https://gitlab.com/gitlab-org/gitlab-pages/issues/129 - gitlab_pages['inplace_chroot'] = true # Set listen-proxy when behind reverse proxy. see https://docs.gitlab.com/ee/administration/pages/#configure-listener-for-reverse-proxy-requests gitlab_pages['listen_proxy'] = "0.0.0.0:8090" - # Fix for https://gitlab.com/gitlab-org/gitlab-pages/-/issues/534 + # Required due to https://gitlab.com/gitlab-org/gitlab-pages/issues/129 + # "chroot bind mount breaks containerised Omnibus deployment" + gitlab_pages['inplace_chroot'] = true + # Fix for https://gitlab.com/gitlab-org/gitlab-pages/-/issues/534 causing failed DNS queries gitlab_pages['internal_gitlab_server'] = "http://127.0.0.1" # Prevent conflict with host SSH port diff --git a/150_gitlab/100_reverse_proxy/slides.md b/150_gitlab/100_reverse_proxy/slides.md index ce1aabcd..c22b4c6f 100644 --- a/150_gitlab/100_reverse_proxy/slides.md +++ b/150_gitlab/100_reverse_proxy/slides.md @@ -22,6 +22,10 @@ Docker Template [](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template) +### Alternative + +Community container image [](https://github.com/sameersbn/docker-gitlab) + --- ## Reverse Proxy in front of GitLab @@ -49,6 +53,7 @@ Deploy using `docker compose` v2: # Clone repository with slides and demos git clone https://github.com/nicholasdille/container-slides cd container-slides/150_gitlab/100_reverse_proxy +git pull # Remove existing instance of GitLab docker rm -f gitlab @@ -62,7 +67,7 @@ Your VM has the necessary environment variables: `DOMAIN` and `IP` Extract password (or [reset](#/gitlab_troubleshooting)): ```bash -docker compose --project-name gitlab exec gitlab +docker compose --project-name gitlab exec gitlab \ cat /etc/gitlab/initial_root_password \ | grep ^Password | cut -d' ' -f2 ``` @@ -83,6 +88,9 @@ Purge data by removing volumes: docker volume rm gitlab_config docker volume rm gitlab_logs docker volume rm gitlab_data +docker volume create gitlab_config +docker volume create gitlab_logs +docker volume create gitlab_data ``` A fresh instance has a new initial root password diff --git a/150_gitlab/135_integrations/compose.yml b/150_gitlab/135_integrations/compose.yml index 4725ddfc..740976a9 100644 --- a/150_gitlab/135_integrations/compose.yml +++ b/150_gitlab/135_integrations/compose.yml @@ -3,7 +3,7 @@ services: kroki: - image: yuzutech/kroki:0.17.0 + image: yuzutech/kroki:0.25.0 environment: KROKI_BLOCKDIAG_HOST: blockdiag KROKI_MERMAID_HOST: mermaid @@ -11,13 +11,13 @@ services: KROKI_EXCALIDRAW_HOST: excalidraw blockdiag: - image: yuzutech/kroki-blockdiag:0.17.0 + image: yuzutech/kroki-blockdiag:0.25.0 mermaid: - image: yuzutech/kroki-mermaid:0.17.0 + image: yuzutech/kroki-mermaid:0.25.0 bpmn: - image: yuzutech/kroki-bpmn:0.17.0 + image: yuzutech/kroki-bpmn:0.25.0 excalidraw: - image: yuzutech/kroki-excalidraw:0.17.0 + image: yuzutech/kroki-excalidraw:0.25.0 diff --git a/150_gitlab/140_troubleshooting/slides.md b/150_gitlab/140_troubleshooting/slides.md index b5d5c07c..e95c4e3c 100644 --- a/150_gitlab/140_troubleshooting/slides.md +++ b/150_gitlab/140_troubleshooting/slides.md @@ -41,7 +41,8 @@ But you have access to the console Use `gitlab-rake`: ```bash -docker exec -it gitlab gitlab-rake "gitlab:password:reset[root]" +docker exec -it gitlab \ + gitlab-rake "gitlab:password:reset[root]" ``` --- @@ -72,7 +73,9 @@ ssh -Tvvv -i id_rsa git@gitlab. Find user for given SSH key fingerprint: ```bash -curl --silent --header "Private-Token: admin-private-token" https://gitlab.haufedev.systems/api/v4/keys?fingerprint=d0:6d:2e:bb:fb:27:f1:6e:80:6c:16:b2:be:c6:d8:00 | jq +curl --silent --header "Private-Token: admin-private-token" \ + https://gitlab.example.com/api/v4/keys?fingerprint=d0:6d:2e:bb:fb:27:f1:6e:80:6c:16:b2:be:c6:d8:00 \ +| jq ``` --- @@ -96,10 +99,11 @@ curl -sH "Private-Token: " http://gitlab./api/v4/user \ Example for group access token (group ID 6): ```bash -curl -sH "Private-Token: " http://gitlab./api/v4/user | jq -r .username +curl -sH "Private-Token: " http://gitlab./api/v4/user \ +| jq -r .username group_6_bot ``` ### Deploy Token -No known way to find group or project +No known way to find group or project... except for log parsing for requests diff --git a/150_gitlab/150_update/slides.md b/150_gitlab/150_update/slides.md index ba833352..cbdb0aab 100644 --- a/150_gitlab/150_update/slides.md +++ b/150_gitlab/150_update/slides.md @@ -37,4 +37,4 @@ You must update to every minor version due to schema updates 1. Update `compose.yml` in 100_reverse_proxy
with new image `gitlab/gitlab-ce:16.9.2-ce.0` 1. Run deployment from [reverse proxy section](#/gitlab_traefik) -(Minor update to v15.10.0 possible.) +(Minor update to v16.10.0 possible.) diff --git a/150_gitlab/160_runner/slides.md b/150_gitlab/160_runner/slides.md index 8c97a451..6eb92300 100644 --- a/150_gitlab/160_runner/slides.md +++ b/150_gitlab/160_runner/slides.md @@ -24,15 +24,17 @@ Jobs select runners by specifying a tag --- -## Heads-up +## Heads-up: New runner registration -New runner registration process [](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) + + +Available since 15.10 [](https://docs.gitlab.com/ee/architecture/blueprints/runner_tokens/) New default in 16.0 (May 2023) -Enforcement in 16.6 (November 2023) +Old runner registration behind disabled feature flag in 17.0 -Removal of old runner registration in 17.0 (May 2024) +Removal of old runner registration in 18.0 (May 2025) ### Old process @@ -42,7 +44,7 @@ Credential leak causes a lot of work ### New process [](https://docs.gitlab.com/ee/ci/runners/new_creation_workflow.html) -Create a runner through the UI or the API +Create a runner through the UI [](https://docs.gitlab.com/ee/ci/runners/runners_scope.html) or the API [](https://docs.gitlab.com/ee/api/users.html#create-a-runner-linked-to-a-user) One token per runner @@ -81,7 +83,7 @@ docuum [](https://github.c cd ../160_runners # Deploy GitLab runner - export CI_SERVER_URL=https://gitlab.inmylab.de + export CI_SERVER_URL=https://gitlab.seat.inmylab.de export CI_SERVER_TOKEN= export RUNNER_EXECUTOR=docker docker compose --project-name gitlab \ diff --git a/150_gitlab/170_monitoring/compose.yml b/150_gitlab/170_monitoring/compose.yml index 9f10bae0..3757a2d9 100644 --- a/150_gitlab/170_monitoring/compose.yml +++ b/150_gitlab/170_monitoring/compose.yml @@ -1,7 +1,7 @@ services: grafana: - image: grafana/grafana:10.2.2 + image: grafana/grafana:10.2.6 environment: GF_SECURITY_ADMIN_USER: seat GF_SECURITY_ADMIN_PASSWORD: "${SEAT_PASS:?You must supply SEAT_PASS}" diff --git a/150_gitlab/170_monitoring/slides.md b/150_gitlab/170_monitoring/slides.md index 333b87b9..4065527f 100644 --- a/150_gitlab/170_monitoring/slides.md +++ b/150_gitlab/170_monitoring/slides.md @@ -12,7 +12,7 @@ GitLab ships with Prometheus [](https://docs.gitlab.com/omnibus/settings/grafana.html) -Grafana was removed in 16.3.0 (May 2023) [](https://docs.gitlab.com/ee/update/deprecations.html?removal_milestone=16.3#bundled-grafana-deprecated-and-disabled) +Grafana was removed in 16.3.0 (August 2023) [](https://docs.gitlab.com/ee/update/deprecations.html?removal_milestone=16.3#bundled-grafana-deprecated-and-disabled) All components expose metrics diff --git a/150_gitlab/180_components/compose.yml b/150_gitlab/180_components/compose.yml index 74ffce63..de7241ff 100644 --- a/150_gitlab/180_components/compose.yml +++ b/150_gitlab/180_components/compose.yml @@ -17,7 +17,7 @@ volumes: services: gitlab: - image: gitlab/gitlab-ee:16.5.2-ee.0 + image: gitlab/gitlab-ee:16.9.2-ee.0 environment: # https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template GITLAB_OMNIBUS_CONFIG: | @@ -73,7 +73,7 @@ services: traefik.tcp.routers.ssh.service: ssh db: - image: gitlab/gitlab-ee:16.5.2-ee.0 + image: gitlab/gitlab-ee:16.9.2-ee.0 environment: # https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template GITLAB_OMNIBUS_CONFIG: | @@ -110,7 +110,7 @@ services: - postgres-log:/var/log/gitlab redis: - image: gitlab/gitlab-ee:16.5.2-ee.0 + image: gitlab/gitlab-ee:16.9.2-ee.0 environment: # https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template GITLAB_OMNIBUS_CONFIG: | @@ -137,7 +137,7 @@ services: - redis-log:/var/log/gitlab gitaly: - image: gitlab/gitlab-ee:16.5.2-ee.0 + image: gitlab/gitlab-ee:16.9.2-ee.0 environment: # https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template GITLAB_OMNIBUS_CONFIG: | diff --git a/150_gitlab/190_database/slides.md b/150_gitlab/190_database/slides.md index 314a9db5..16b4bc49 100644 --- a/150_gitlab/190_database/slides.md +++ b/150_gitlab/190_database/slides.md @@ -6,6 +6,30 @@ --- +## Database + + + +Only PostgreSQL is supported [](https://docs.gitlab.com/omnibus/settings/database.html) +- Packaged Linux packages as well as Docker +- External instances are supported [](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server) + +Mind the version requirements [](https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements) + +### Multiple connections since v16 + +GitLab is preparing a separate database for CI/CD features + +Two connections to single database since 16.0 (May 2023) + +Disable second connection to single database [](https://docs.gitlab.com/omnibus/settings/database.html#configuring-multiple-database-connections) + +Separate database required in 18.0 (May 2025) + +Migration to second database is not ready for production yet [](https://docs.gitlab.com/ee/administration/postgresql/multiple_databases.html) + +--- + ## Important database tables diff --git a/2024-04-10_heise-GitLab-Ops.html b/2024-04-10_heise-GitLab-Ops.html index 5e931cee..8bfe8000 100644 --- a/2024-04-10_heise-GitLab-Ops.html +++ b/2024-04-10_heise-GitLab-Ops.html @@ -45,6 +45,8 @@ Haufe Group Docker Captain + +![](images/logos/heise-academy.svg)
@@ -178,6 +180,52 @@
+
+ +
+
diff --git a/2024-04-10_heise-GitLab-Ops.md b/2024-04-10_heise-GitLab-Ops.md new file mode 100644 index 00000000..c7cc7326 --- /dev/null +++ b/2024-04-10_heise-GitLab-Ops.md @@ -0,0 +1,4 @@ +- Enforce DNS record +- seatctl: list should check DNS +- Image for demo env +- Pre-create external users?! \ No newline at end of file diff --git a/images/logos/heise-academy.png b/images/logos/heise-academy.png new file mode 100755 index 0000000000000000000000000000000000000000..24dd092b0ce989767485eaa151308b77c615b2e0 GIT binary patch literal 5326 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 quVboqiOotfIwZO1i32_MmY_ch_bn_