Files
computer-playbook/roles/web-app-gitlab/tasks
kevinveenbirkenbachandClaude Opus 5 7c5c7a31fc fix(web-app-gitlab): mount the shell secret where Rails looks for it
gitlab.yml.j2:100-103 splits the two paths: gitlab_shell.path is /home/git/gitlab-shell/, which lives in the container's ephemeral layer, while secret_file points at the read-only mounted secret under /var/opt. lib/gitlab/shell.rb:45 returns early only when path/.gitlab_shell_secret exists, so on every boot of every fresh container that guard is false and generate_and_link_secret_token runs. Its own protection is a TOCTOU: ln_s(force: true) swallows the error from remove_file and then calls File.symlink, so two Rails processes in the same container both get past the check and the loser dies.

Observed in run 38673950436, job 116229890606, at the role's last task: Errno::EEXIST from symlink on /home/git/gitlab-shell/.gitlab_shell_secret, raised at lib/gitlab/shell.rb:90 in generate_and_link_secret_token, reached from lib/gitlab/shell.rb:47 in ensure_secret_token!. Frame 47 proves the line-45 guard did not fire.

The same secret is now mounted at that path as well, for webservice, sidekiq and migrations, which share one gitlab.yml. The guard is true from PID 1, ln_sf is never reached, and the race is removed rather than narrowed. Nothing reads the new mount: secret_token still resolves through secret_file, so the added path only flips one boolean.

It lands as its own shell_path_secret entry rather than three more mounts on shell_secret. One volume entry mounted twice into the same service has no precedent anywhere in the repository, and container_volumes.py would emit two secrets: refs sharing one source into a single service; the separate entry keeps one ref per service, the shape the other 27 entries in this file already deploy. compose_volumes.py names it gitlab_shell_path_secret_<sha8>, distinct from gitlab_shell_secret_<sha8> but hashed from the same source, so both still rotate together.

5fdaba6af2 timed around the race from inside 03_design.yml. That wait keeps its value, since the MCP flush recreates the containers and the appearance step then drives webservice, so it moves to 00_core.yml directly after the step that destroys readiness and widens to the role's five-service 02_wait.yml.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-15 12:19:41 +02:00
..
…