Commit Graph
10 Commits
Author SHA1 Message Date
Alejandro Roman e6596d51f2 fix(stalwart): add the fix PR into main PR 2026-09-09 00:21:58 +04:00
Alejandro Roman 7b573748d4 fix(stalwart): Fix nextcloud issues related to mail + gitlab 2026-09-07 03:50:25 +04:00
Alejandro Roman e809765e23 fix(stalwart): fix all comments, fix a tor issue fouind and fix all SPOT issues 2026-09-02 15:02:46 +04:00
Alejandro Roman 333079cc2a feat(UI): fix migration, errors happening in local deploy and swarm integraton + merged main 2026-08-30 21:39:52 +04:00
kevinveenbirkenbachandClaude Opus 5 2389b6e38b fix(gitlab): discover the OIDC issuer over the scheme it actually uses
GitLab could not log anyone in through Keycloak on an onion deployment. The sign-in page carried the omniauth flash: Could not authenticate you from OpenIDConnect because Ssl connect returned=1 errno=0 peeraddr=172.30.0.10:443 state=error: tlsv1 unrecognized name (ssl alert number 112), and gitlab-webservice logged OpenIDConnect::Discovery::DiscoveryFailed eleven times.

The openid_connect gem rebuilds the well-known URL and never reads the scheme of the issuer. Its config resource also drops the port when it is 80 or 443, so http://auth.onion loses port 80 and URI::HTTPS.build puts 443 back. The hidden service publishes port 80 only, so 443 reaches the SNI-less default listener, which answers ssl_reject_handshake - alert 112, exactly the string in the flash.

An initializer sets SWD.url_builder and WebFinger.url_builder to URI::HTTP when the issuer is plaintext. These are the same two lines web-app-decidim already ships for the identical bug, and this repo has one such fix per role for zammad, mastodon, decidim and peertube; gitlab was the gap. The mount copies the smtp_settings.rb initializer pattern onto webservice and sidekiq.

The scheme test lives in the ruby file rather than in the render and mount conditions, which both gate on services.sso.enabled alone. Two separately spelled conditions could drift once an inventory sets OIDC_OVERRIDE.URL, and would then either render a file that is never mounted or mount one that was never rendered.

Boot safety: omniauth_openid_connect and openid_connect are default-group Gemfile entries without require: false, so Bundler.require loads them before the initializers run, and openid_connect requires swd and webfinger itself. Both writers are public and assign an ivar. GitLab never assigns url_builder anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 13:37:57 +02:00
kevinveenbirkenbachandClaude Opus 5 6e93862dd5 refactor(gitlab): ship the config trees as swarm configs, not host binds
0bf1e8ef8 replaced Omnibus with the Cloud Native composition and brought nine
bind-mounted config trees with it. Their source directories are created by
tasks/01_config.yml, undelegated, so only on the play host; the peer-replication
branches in sys-svc-compose/tasks/utils/_render_one_template.yml:36-56 are gated
on `not IS_STACK_HOST` and skip entirely when the play targets the manager
alone. Under swarm any task scheduled on a worker therefore found no bind
source, was Rejected permanently, and stack_ready.sh reported "stuck:
gitlab_shell". The nextcloud-6 job dies there, before its own role runs.

Pinning the role to managers would fix the symptom and give up the distribution
the CNG split exists for. Swarm already solves this: it hands a config or secret
to every node that runs a task, so nothing has to pre-exist anywhere. The nine
trees become 26 per-file entries, and gitaly, shell, workhorse, webservice,
sidekiq and migrations are schedulable again with no placement constraint.

One mount moves on purpose. The gitaly image sets CONFIG_TEMPLATE_DIRECTORY=
/etc/gitaly and ships config.toml.tpl there, and entrypoint.sh calls
`set-config "$CONFIG_TEMPLATE_DIRECTORY" "${CONFIG_DIRECTORY:=$CONFIG_TEMPLATE_DIRECTORY}"`
under `set -eo pipefail` -- so a read-only config at /etc/gitaly/config.toml
would be overwritten and crash-loop the container. Pointing the variable at
/etc/gitaly/templates leaves no .tpl for set-config to expand (nullglob) and
gitaly still finds the file, because process-wrapper derives
GITALY_CONFIG_FILE="${GITALY_CONFIG_FILE:-$CONFIG_TEMPLATE_DIRECTORY/config.toml}".
Verified against registry.gitlab.com/gitlab-org/build/cng/gitaly:v19.2.1.

Every other mount lands where it did before: of the 32 config/secret mounts, 5
match an old target exactly and 27 sit inside the directory an old bind
provided. No mount moved unintentionally.

Repeated targets are anchored once and aliased after, so a rename is one edit.
gitaly-config.toml.j2 and shell-config.yml.j2 read their paths from the registry
through the volume lookup instead of repeating them.

Costs, both deliberate: swarm materialises configs and secrets as root:root and
neither emitter supports uid/gid, while GitLab CNG runs as uid 1000 -- so
secrets.yml, both .gitlab_*_secret files and the SSH host keys are 0444 and
readable by any process inside the container, not just GitLab. And a config
carries a content hash, so a one-character edit to gitlab.yml now rolls three
services instead of rewriting a file in place.

Checked without a deploy: validate_volumes_meta reports no violation; both
emitters produce identical configs: blocks under compose and swarm; a file
mounted inside a named-volume mountpoint survives (measured in a container); the
host keys are exactly the static pair 01_config.yml generates; no script or
binary in gitlab-shell:v19.2.1 refuses a permissive host key, and SSH_DAEMON is
gitlab-sshd rather than OpenSSH. make test-lint: 327 passed.

Not verified: nothing here has been deployed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:29 +02:00
kevinveenbirkenbachandClaude Opus 5 f713ba8350 refactor(credentials): address them at secrets.credentials, like every other meta file
meta/schema.yml became meta/secrets.yml, but its content still landed at
applications.<app>.credentials — the one meta file whose payload did not sit
under a root named after the file. Every other topic obeys that rule:
meta/volumes.yml goes to volumes, meta/rbac.yml to rbac. Credentials now go to
secrets.credentials.

The path exists exactly once, in utils/manager/credential_key.py, and
OVERRIDE_SECTION is derived from its two segments rather than spelled beside
them, so the dotted form used by --set and the nested form used by the
inventory cannot drift. Six consumers read it: the loader, the generator, the
CLI override parser, the two emit helpers, and the four lookup validators.

--set now accepts exactly one key shape,
applications.<app>.secrets.credentials.<key>. The three short forms are
rejected instead of guessed at: the same short key names different credentials
on different apps. That also retired primary_app_id, which only existed to
disambiguate them.

Three defects surfaced while wiring this up. _apply_one_role_schema still wrote
to the old root, so an app's own credentials would have moved while its
providers' stayed behind. web-app-pgadmin reads through
lookup('applications', ...).credentials.<key>, an access path no regex over
lookup('config', ...) could see, and would have deployed with an empty postgres
password. And _META_NON_GENERIC_TOPICS still listed 'schema' — a bare topic
name, invisible to any path search — so the generic file-root loop assigned the
raw schema under secrets and the follow-up write landed inside the cached
document, truncating keycloak's four credentials to one for every later
reader.

tests/integration/ansible/lookups/config/test_credentials_roundtrip.py pins both
directions of the seam and the cache invariant that last defect broke.

Existing inventories still carry the pre-rename root; migrating them is
deliberately out of scope here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:51:56 +02:00
Alejandro Roman ac101d2efa Merge kevinveenbirkenbach/feature/swarm-nfs into stalwart integration branch 2026-07-21 10:00:17 +02:00
kevinveenbirkenbachandClaude Fable 5 40e553069d refactor(structure): nest prefix-grouped sibling files into subfolders
Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations.

Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 20:00:03 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0bf1e8ef8c refactor(gitlab): replace Omnibus with the Cloud Native GitLab composition
Deploy GitLab from the official CNG CE images as six services: webservice (puma) and sidekiq scale to the topology default, workhorse is the sole HTTP entry, gitaly and a one-shot migrations job are pinned singletons, shell runs gitlab-sshd. The Omnibus container, its Dockerfile and the GITLAB_OMNIBUS_* config groups are removed. All rails/workhorse/shell/gitaly configs and shared secrets are pre-rendered by Ansible and mounted read-only; eleven credentials land in meta/schema.yml including an rsa_pem_2048 OIDC signing key (new value_generator algorithm). Postgres, redis and S3 object storage ride the existing lookups; OIDC, SMTP, the public SSH port and the root password are preserved. compose_volumes gains a per-volume nfs:false opt-out so the gitaly repositories volume stays node-local in swarm (upstream forbids git repositories on NFS); nfs_prep skips volumes without driver_opts accordingly. The Nextcloud gitlab integration execs the CNG rails runner. ruby_single_line dies with its only consumer; ruby_dq keeps test coverage. Ports (8181/8080/2222/8075/3808/9122) and service names resolve from single SPOT declarations in meta/services.yml via vars constants; the version pin sits on the webservice image pair so the updater can bump it. Live-validated in compose up to all six services healthy and migrations completed; fresh-install-only semantics for existing Omnibus data are documented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 02:37:27 +02:00