From 13cff04600b390c66a6aec4d032ec8dd4546dde6 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 10 Jun 2026 16:06:40 +0200 Subject: [PATCH 1/5] feat(topology): add admin-rocketchat box template + bootstrap role Add an admin-rocketchat box template (Debian, template-vm-debian-trixie-medium) that bootstraps the existing 03_container_layer/docker/admin/rocketchat compose stack: firewall (22/3000) -> Docker baseline -> software.install.rocketchat. - New role software.install.rocketchat: rsyncs the catalog Rocket.Chat stack onto the box and brings it up, delegating to software.configure.docker-compose (no stack duplication; env-lookup path lives in role defaults, not in the injection-guarded box-template params). - software.configure.docker-compose: was Ubuntu-only (silent no-op on Debian); broadened the deploy gate to ['Ubuntu','Debian'] and renamed tasks/ubuntu -> tasks/debian-based to match the basic_packages convention. Tasks are apt/rsync based, so behaviour on Ubuntu is unchanged. --- .../tasks/{ubuntu => debian-based}/deploy.yml | 0 .../tasks/main.yml | 8 +++-- .../software.install.rocketchat/README.md | 36 +++++++++++++++++++ .../defaults/main.yml | 27 ++++++++++++++ .../software.install.rocketchat/meta/main.yml | 14 ++++++++ .../tasks/main.yml | 23 ++++++++++++ .../admin-rocketchat/v1.0.0/template.yml | 28 +++++++++++++++ 7 files changed, 133 insertions(+), 3 deletions(-) rename 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/{ubuntu => debian-based}/deploy.yml (100%) create mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/README.md create mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml create mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml create mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml create mode 100644 05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml diff --git a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/ubuntu/deploy.yml b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml similarity index 100% rename from 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/ubuntu/deploy.yml rename to 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml diff --git a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml index 925d39c..54bb4d2 100644 --- a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml +++ b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml @@ -7,8 +7,10 @@ ansible.builtin.setup: # -- name: INCLUDE - ufw tasks - ubuntu - ansible.builtin.include_tasks: ./ubuntu/deploy.yml - when: ansible_facts['distribution'] == "Ubuntu" +# Tasks in ./debian-based/deploy.yml are apt/rsync based, so they run on every +# Debian-family host (Ubuntu + Debian). +- name: INCLUDE - deploy tasks - debian-based + ansible.builtin.include_tasks: ./debian-based/deploy.yml + when: ansible_facts['distribution'] in ['Ubuntu', 'Debian'] #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/README.md b/02_ansible_layer/admin/roles/software.install.rocketchat/README.md new file mode 100644 index 0000000..287e235 --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.rocketchat/README.md @@ -0,0 +1,36 @@ +# software.install.rocketchat + +Deploys the **Rocket.Chat** docker-compose stack onto a target box. + +The stack itself (Rocket.Chat + MongoDB replica set + token provisioner) lives in +the catalog at [`03_container_layer/docker/admin/rocketchat/`](../../../../03_container_layer/docker/admin/rocketchat/). +This role rsyncs that directory onto the box and brings it up, delegating the +deploy + `docker compose up` to the shared +[`software.configure.docker-compose`](../software.configure.docker-compose/) role. + +## Requirements + +Docker engine + compose plugin must already be installed on the target. Wire +`software.install.warmup.basic_packages` with `INSTALL_PACKAGES_DOCKER: "YES"` +and `INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"` before this role (the +`admin-rocketchat` box template does this). + +`RANGE42_INVENTORY` must be exported on the controller (done by `range42-context`) +so the stack source resolves. + +## Role variables + +| Variable | Default | Purpose | +|----------|---------|---------| +| `ROCKETCHAT_LOCAL_PROJECT_DIR` | `{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/rocketchat` | Controller-side stack source | +| `ROCKETCHAT_REMOTE_PROJECT_DIR` | `/opt/range42/rocketchat` | Where the stack is staged + run on the box | +| `ROCKETCHAT_OPERATOR_USER` | `{{ default_admin_vm_ci_user }}` | Owner of staged files (scenario cloud-init admin user) | +| `ROCKETCHAT_CONTAINER_NAME` | `rocketchat` | Main container polled after `up` | +| `ROCKETCHAT_LABEL_PROJECT_TYPE` | `admin` | Label only | + +## Notes + +The compose stack ships sane defaults (admin creds, `ROOT_URL`, `HTTP_PORT=3000`) +via `.env.example`; override by editing the stack's `.env` before deploy. The web +UI listens on port 3000 — open it on the box firewall (the `admin-rocketchat` box +template does). diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml new file mode 100644 index 0000000..dafc00b --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml @@ -0,0 +1,27 @@ +--- +# software.install.rocketchat — deploy the Rocket.Chat docker-compose stack. +# +# The stack itself lives in the catalog at +# 03_container_layer/docker/admin/rocketchat/ +# (Rocket.Chat + MongoDB replica set + provisioner). This role rsyncs that +# directory onto the target box and brings it up via docker compose, reusing the +# shared software.configure.docker-compose role. + +# Controller-side source: resolved from the workspace RANGE42_INVENTORY env var +# (exported by range42-context). Mirrors how CTF stacks resolve +# RANGE42_INVENTORY__DOCKER__CTF at deploy time. +ROCKETCHAT_LOCAL_PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/rocketchat" + +# Where the stack is staged + run on the target box. +ROCKETCHAT_REMOTE_PROJECT_DIR: "/opt/range42/rocketchat" + +# Owner of the staged files on the box: the scenario cloud-init admin user. +# Matches the canonical OPERATOR_USER wiring (no fallback — fail loudly if the +# scenario never set it, rather than silently chown to a user that may not exist). +ROCKETCHAT_OPERATOR_USER: "{{ default_admin_vm_ci_user }}" + +# Container name docker-compose polls for after `up` — matches compose.yml. +ROCKETCHAT_CONTAINER_NAME: "rocketchat" + +# Label only; admin service (not a CTF target). +ROCKETCHAT_LABEL_PROJECT_TYPE: "admin" diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml new file mode 100644 index 0000000..62522da --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml @@ -0,0 +1,14 @@ +--- +dependencies: [] +galaxy_info: + role_name: software_install_rocketchat + namespace: range42 + author: range42 + description: Deploy the Rocket.Chat docker-compose stack (admin collaboration service). + license: GPL-3.0-or-later + min_ansible_version: "2.14" + platforms: + - name: Ubuntu + versions: [noble] + - name: Debian + versions: [trixie] diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml new file mode 100644 index 0000000..eeb2dc7 --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml @@ -0,0 +1,23 @@ +--- +## +## software.install.rocketchat +## +## Deploys the catalog Rocket.Chat docker-compose stack onto the target box by +## delegating to the shared software.configure.docker-compose role. Assumes the +## Docker engine + compose plugin are already present (wire +## software.install.warmup.basic_packages with INSTALL_PACKAGES_DOCKER first). +## + +- name: DEPLOY - rocket.chat docker-compose stack + ansible.builtin.include_role: + name: software.configure.docker-compose + vars: + LOCAL__PROJECT_DIR: "{{ ROCKETCHAT_LOCAL_PROJECT_DIR }}" + REMOTE_PROJECT_DIR: "{{ ROCKETCHAT_REMOTE_PROJECT_DIR }}" + OPERATOR_USER: "{{ ROCKETCHAT_OPERATOR_USER }}" + LABEL_PROJECT_TYPE: "{{ ROCKETCHAT_LABEL_PROJECT_TYPE }}" + LABEL_PROJET_NAME: "{{ ROCKETCHAT_CONTAINER_NAME }}" + SEND_POC_DIR: "NO" + CLEAN_UP_DEPLOY_DIR: "NO" + +#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### diff --git a/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml b/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml new file mode 100644 index 0000000..80c311b --- /dev/null +++ b/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml @@ -0,0 +1,28 @@ +id: admin-rocketchat +api_version: 1 +description: >- + Rocket.Chat admin box on Debian — provisions the Docker baseline, opens the + service port, and bootstraps the Rocket.Chat compose stack (Rocket.Chat + + MongoDB replica set + provisioner) from + 03_container_layer/docker/admin/rocketchat via software.install.rocketchat. +template_vm: "template-vm-debian-trixie-medium" +default_attachments: + # host firewall — 22 (ssh) + 3000 (Rocket.Chat HTTP, HTTP_PORT default) + - kind: role + catalog_ref: software.configure.firewalls + params: + firewall_rules: + - {ip: "all", port: 22, protocol: "tcp"} # ssh + - {ip: "all", port: 3000, protocol: "tcp"} # rocketchat web ui + # Docker baseline: engine + compose plugin + operator utilities + - kind: role + catalog_ref: software.install.warmup.basic_packages + params: + INSTALL_PACKAGES_BASICS: "YES" + INSTALL_PACKAGES_DOCKER: "YES" + INSTALL_PACKAGES_DOCKER_COMPOSE: "YES" + INSTALL_PACKAGES_UTILS_JSON: "YES" + INSTALL_PACKAGES_UTILS_NETWORK: "YES" + INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES" + # Bootstrap Rocket.Chat: rsync the catalog compose stack + docker compose up + - {kind: role, catalog_ref: software.install.rocketchat, params: {}} From 752b5df490e88e6eefaff99cb95a6f633a97f8c6 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 10 Jun 2026 20:53:48 +0200 Subject: [PATCH 2/5] fix(rocketchat): bring branch up to the working deploy state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync the rocketchat-deployable fixes proven on dev_ada so PR #189 reflects a working end-to-end deploy on Debian: - admin-rocketchat box template: drop NTP-and-update-time (no reachable NTP in egress-filtered ranges; host kvm-clock is correct). - software.install.warmup.basic_packages docker path: Debian-clean — drop Ubuntu-only software-properties-common, use keyring instead of removed apt-key, install the docker compose v2 plugin + python Docker SDK (ubuntu/ tasks -> debian-based/). - software.configure.docker-compose: retry `docker compose up` on transient registry/mirror timeouts. - rocketchat stack: MongoDB 6.0 -> 8.0 (rocket.chat:latest is 8.4.3, needs >=8); Node-based healthcheck on /health + provisioner curl + /api/info readiness (rocket.chat image ships no curl; /api/v1/info is 404 on 8.x). Deployed clean: Rocket.Chat 8.4.3 healthy on :3000 with provisioning. --- .../tasks/debian-based/deploy.yml | 8 ++ .../tasks/include/docker/_main.yaml | 12 +-- .../include/docker/debian-based/docker.yaml | 80 +++++++++++++++++++ .../docker/debian-based/docker_compose.yaml | 58 ++++++++++++++ .../tasks/include/docker/ubuntu/docker.yaml | 66 --------------- .../include/docker/ubuntu/docker_compose.yaml | 57 ------------- .../docker/admin/rocketchat/Dockerfile | 4 + .../docker/admin/rocketchat/compose.yml | 14 +++- .../admin/rocketchat/provisioning/init.sh | 2 +- .../admin-rocketchat/v1.0.0/template.yml | 6 +- 10 files changed, 172 insertions(+), 135 deletions(-) create mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml create mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml delete mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml delete mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml diff --git a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml index 9c5dcfb..622f2cd 100644 --- a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml +++ b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml @@ -51,10 +51,18 @@ block: # # + # Retry on transient failures (intermittent registry/mirror anycast timeouts + # pulling images are common on egress-filtered ranges). `up` is idempotent, + # so re-running resumes the pull. A persistent failure still surfaces after + # the retries are exhausted. - name: DOCKER - DOCKER-COMPOSE - RUN community.docker.docker_compose_v2: project_src: "{{ REMOTE_PROJECT_DIR }}" state: present + register: compose_run + until: compose_run is succeeded + retries: 5 + delay: 20 # - name: DOCKER - GET CONTAINER INFO {{ LABEL_PROJET_NAME }} community.docker.docker_container_info: diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml index 35a0901..295054e 100644 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml @@ -3,9 +3,9 @@ - name: INCLUDE - install - docker block: # - - name: install - docker - ubuntu - ansible.builtin.include_tasks: ./ubuntu/docker.yaml - when: ansible_facts.distribution == 'Ubuntu' + - name: install - docker - debian-based + ansible.builtin.include_tasks: ./debian-based/docker.yaml + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] # - name: install - docker - fedora ansible.builtin.include_tasks: ./fedora/docker.yaml @@ -18,9 +18,9 @@ - name: INCLUDE - install - docker-compose block: # - - name: install - docker-compose - ubuntu - ansible.builtin.include_tasks: ./ubuntu/docker_compose.yaml - when: ansible_facts.distribution == 'Ubuntu' + - name: install - docker-compose - debian-based + ansible.builtin.include_tasks: ./debian-based/docker_compose.yaml + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] # - name: install - docker-compose - fedora ansible.builtin.include_tasks: ./fedora/docker_compose.yaml diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml new file mode 100644 index 0000000..88cfdd6 --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml @@ -0,0 +1,80 @@ +--- +- name: install docker - requirements - fedora + block: + # + - name: install docker - requirements for docker-compose and docker - debian-based + ansible.builtin.apt: + name: + - apt-transport-https + - ca-certificates + - curl + # software-properties-common: Ubuntu-only here — absent from Debian + # trixie 'main', and unused anyway (the Docker repo is added via the + # apt_repository/apt_key modules, not add-apt-repository). + - make + - gcc + - python3-pip + - python3-dev + - libffi-dev + - libssl-dev + state: present + + #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + + # apt-key is removed on Debian 12+/trixie (and deprecated on Ubuntu), so the + # apt_key module fails ("apt-key not found"). Use the modern keyring method: + # fetch the armored key into /etc/apt/keyrings and pin it via signed-by=. + - name: install docker - ensure /etc/apt/keyrings exists + ansible.builtin.file: + path: /etc/apt/keyrings + state: directory + mode: "0755" + + # + - name: install docker - add docker official GPG key (keyring) + ansible.builtin.get_url: + url: https://download.docker.com/linux/{{ ansible_facts.distribution | lower }}/gpg + dest: /etc/apt/keyrings/docker.asc + mode: "0644" + + # + - name: install docker - add Docker CE repository + ansible.builtin.apt_repository: + repo: "deb [arch={{ 'arm64' if ansible_facts.architecture == 'aarch64' else 'amd64' }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_facts.distribution | lower }} {{ ansible_distribution_release }} stable" + filename: docker + state: present + + # + - name: install docker - docker-ce - debian-based + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + update_cache: yes + state: present + + # + - name: install docker - enable docker service + ansible.builtin.systemd: + name: docker + enabled: yes + state: started + + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] + +#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + +- name: check docker - checks debug - debian-based + block: + - name: check - docker installation + ansible.builtin.command: + cmd: docker version + register: result + + - name: check - docker version + ansible.builtin.debug: + msg: "{{ result.stdout }}" + when: result is defined + + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml new file mode 100644 index 0000000..f86c602 --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml @@ -0,0 +1,58 @@ +--- +# +- name: install docker-compose - requirements - debian-based + block: + # + - name: install docker-compose - requirements - debian-based + ansible.builtin.apt: + name: + - apt-transport-https + - ca-certificates + - curl + # software-properties-common: Ubuntu-only — absent from Debian trixie + # 'main' and unused here (repo added via apt_repository + signed-by). + - make + - gcc + - python3-pip + - python3-dev + - libffi-dev + - libssl-dev + # python Docker SDK — required by the community.docker modules + # (docker_compose_v2 / docker_container_info) used to deploy stacks. + - python3-docker + state: present + + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] + +# +# Docker Compose v2 is the `docker compose` CLI plugin (not the old standalone +# `docker-compose` binary). community.docker.docker_compose_v2 shells out to +# `docker compose`, so install the official plugin from the docker-ce repo +# (added by docker.yaml). The previous standalone-binary download used a v1-era +# asset name (docker-compose-Linux-x86_64) that 404s for v2 releases. +- name: install docker-compose - compose v2 plugin - debian-based + ansible.builtin.apt: + name: + - docker-compose-plugin + update_cache: true + state: present + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] + +#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + +- name: check docker-compose - checks debug - debian-based + block: + # + - name: check - docker compose v2 installation + ansible.builtin.command: + cmd: docker compose version + register: result + changed_when: false + + # + - name: check - docker compose version + ansible.builtin.debug: + msg: "{{ result.stdout }}" + when: result is defined + + when: ansible_facts.distribution in ['Ubuntu', 'Debian'] diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml deleted file mode 100644 index 6f1f200..0000000 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml +++ /dev/null @@ -1,66 +0,0 @@ ---- -- name: install docker - requirements - fedora - block: - # - - name: install docker - requirements for docker-compose and docker - ubuntu - ansible.builtin.apt: - name: - - apt-transport-https - - ca-certificates - - curl - - software-properties-common - - make - - gcc - - python3-pip - - python3-dev - - libffi-dev - - libssl-dev - state: present - - #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - - - name: install docker - add docker official GPG key - ansible.builtin.apt_key: - url: https://download.docker.com/linux/ubuntu/gpg - state: present - - # - - name: install docker - add Docker CE repository - ansible.builtin.apt_repository: - repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable - state: present - - # - - name: install docker - docker-ce - ubuntu - ansible.builtin.apt: - name: - - docker-ce - - docker-ce-cli - - containerd.io - update_cache: yes - state: present - - # - - name: install docker - enable docker service - ansible.builtin.systemd: - name: docker - enabled: yes - state: started - - when: ansible_facts.distribution == 'Ubuntu' - -#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - -- name: check docker - checks debug - ubuntu - block: - - name: check - docker installation - ansible.builtin.command: - cmd: docker version - register: result - - - name: check - docker version - ansible.builtin.debug: - msg: "{{ result.stdout }}" - when: result is defined - - when: ansible_facts.distribution == 'Ubuntu' diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml deleted file mode 100644 index a3408ec..0000000 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml +++ /dev/null @@ -1,57 +0,0 @@ ---- -# -- name: install docker - requirements - fedora - block: - # - - name: install docker - requirements for docker-compose and docker - ubuntu - ansible.builtin.apt: - name: - - apt-transport-https - - ca-certificates - - curl - - software-properties-common - - make - - gcc - - python3-pip - - python3-dev - - libffi-dev - - libssl-dev - state: present - - when: ansible_facts.distribution == 'Ubuntu' - -# -- name: install docker-compose - get_url docker-compose - block: - # - - name: DOWNLOAD - docker-compose - ansible.builtin.get_url: - url: "https://github.com/docker/compose/releases/download/v{{ DOCKER_COMPOSE_VERSION | default('2.39.1') }}/docker-compose-Linux-x86_64" - dest: "/usr/local/bin/docker-compose" - mode: "0755" - - # - - name: install docker-compose - chmod +x docker-compose - ansible.builtin.file: - path: /usr/local/bin/docker-compose - mode: "u+x,g+x" - - when: ansible_facts.distribution == 'Ubuntu' - -#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - -- name: check docker-compose - checks debug - ubuntu - block: - # - - name: check - docker-compose installation - ansible.builtin.command: - cmd: docker-compose --version - register: result - - # - - name: check - docker-compose version - ansible.builtin.debug: - msg: "{{ result.stdout }}" - when: result is defined - - when: ansible_facts.distribution == 'Ubuntu' diff --git a/03_container_layer/docker/admin/rocketchat/Dockerfile b/03_container_layer/docker/admin/rocketchat/Dockerfile index 551f027..e42bfa6 100644 --- a/03_container_layer/docker/admin/rocketchat/Dockerfile +++ b/03_container_layer/docker/admin/rocketchat/Dockerfile @@ -21,6 +21,10 @@ FROM rocketchat/rocket.chat:latest AS runtime USER root +# init.sh drives the Rocket.Chat REST API with curl, but the rocket.chat image +# (Alpine) ships neither curl nor wget — add curl so provisioning can run. +RUN apk add --no-cache curl + COPY --from=builder /usr/bin/yq /usr/bin/yq COPY --from=builder /usr/bin/jq /usr/bin/jq COPY --from=builder /provisioning/ /provisioning/ diff --git a/03_container_layer/docker/admin/rocketchat/compose.yml b/03_container_layer/docker/admin/rocketchat/compose.yml index 15d92a7..b17ac3a 100644 --- a/03_container_layer/docker/admin/rocketchat/compose.yml +++ b/03_container_layer/docker/admin/rocketchat/compose.yml @@ -5,7 +5,7 @@ services: mongodb: - image: mongo:6.0 + image: mongo:8.0 container_name: rocketchat-mongodb command: mongod --replSet rs0 --oplogSize 128 volumes: @@ -19,7 +19,7 @@ services: restart: unless-stopped mongo-init-replica: - image: mongo:6.0 + image: mongo:8.0 container_name: rocketchat-mongo-init command: > bash -c " @@ -59,7 +59,15 @@ services: mongo-init-replica: condition: service_completed_successfully healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/v1/info"] + # The rocket.chat image ships Node (it runs `node main.js`) but NOT curl, + # so a curl-based probe never passes and the container is stuck + # "health: starting" forever (blocking the provisioner via depends_on). + # Use Node's built-in fetch (Node 18+) to hit the info endpoint instead. + test: + - CMD + - node + - -e + - "fetch('http://localhost:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))" interval: 20s timeout: 10s retries: 15 diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/init.sh b/03_container_layer/docker/admin/rocketchat/provisioning/init.sh index 1b7f14f..0dc2e1c 100644 --- a/03_container_layer/docker/admin/rocketchat/provisioning/init.sh +++ b/03_container_layer/docker/admin/rocketchat/provisioning/init.sh @@ -23,7 +23,7 @@ echo "[init] Waiting for Rocket.Chat at ${RC_URL} ..." attempts=0 max_attempts=60 -until curl -sf "${RC_URL}/api/v1/info" >/dev/null 2>&1; do +until curl -sf "${RC_URL}/api/info" >/dev/null 2>&1; do attempts=$((attempts + 1)) if [ "${attempts}" -ge "${max_attempts}" ]; then echo "[init] ERROR: Rocket.Chat did not become healthy after $((max_attempts * 3))s. Aborting." diff --git a/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml b/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml index 80c311b..4fbacf8 100644 --- a/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml +++ b/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml @@ -14,7 +14,10 @@ default_attachments: firewall_rules: - {ip: "all", port: 22, protocol: "tcp"} # ssh - {ip: "all", port: 3000, protocol: "tcp"} # rocketchat web ui - # Docker baseline: engine + compose plugin + operator utilities + # Docker baseline: engine + compose plugin + operator utilities. + # NTP sync is intentionally NOT enabled: in an air-gapped / egress-filtered + # range, udp/123 is blocked, systemd-timesyncd never reaches a server, and the + # role's hard "wait for NTPSynchronized" fails. VMs take host (kvm-clock) time. - kind: role catalog_ref: software.install.warmup.basic_packages params: @@ -23,6 +26,5 @@ default_attachments: INSTALL_PACKAGES_DOCKER_COMPOSE: "YES" INSTALL_PACKAGES_UTILS_JSON: "YES" INSTALL_PACKAGES_UTILS_NETWORK: "YES" - INSTALL_PACKAGES_NTP_AND_UPDATE_TIME: "YES" # Bootstrap Rocket.Chat: rsync the catalog compose stack + docker compose up - {kind: role, catalog_ref: software.install.rocketchat, params: {}} From 5191f839dfdddab7234487af9d6eef2951882134 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Fri, 12 Jun 2026 09:41:37 +0200 Subject: [PATCH 3/5] refactor(rocketchat): replace monolithic init.sh with split provisioner scripts Mirrors the misp-standalone provisioner pattern: dedicated one-shot Alpine provisioner service with scripts volume-mounted at runtime (not baked in), team/org-aware env config, auto-generated passwords, and structured credentials output. - Remove provisioning/init.sh + provisioning/users.yml - Add provision.sh (orchestrator, ENTRYPOINT), provision-users.sh (creates instructor + team accounts, writes /tokens/rc-credentials.json + .provisioned stamp), provision-tokens.sh (generates PATs, writes /tokens/tokens.txt) - Dockerfile: simplify to Alpine + bash/curl/jq/openssl, no COPY - compose.yml: provisioner uses env_file + 3 read-only volume mounts - .env.example: add RC_TEAMS, RC_INSTRUCTOR_ORG, RC_INSTRUCTOR_COUNT, RC_USERS_PER_TEAM, RC_USER_DOMAIN - Makefile: add keys target (rc-credentials.json), update help text - README.md: document new architecture, env vars, both output files --- .../docker/admin/rocketchat/.env.example | 23 +- .../docker/admin/rocketchat/Dockerfile | 32 +-- .../docker/admin/rocketchat/Makefile | 8 +- .../docker/admin/rocketchat/README.md | 166 +++++++------ .../docker/admin/rocketchat/compose.yml | 9 +- .../admin/rocketchat/provisioning/init.sh | 221 ------------------ .../provisioning/provision-tokens.sh | 79 +++++++ .../provisioning/provision-users.sh | 175 ++++++++++++++ .../rocketchat/provisioning/provision.sh | 7 + .../admin/rocketchat/provisioning/users.yml | 22 -- 10 files changed, 399 insertions(+), 343 deletions(-) delete mode 100644 03_container_layer/docker/admin/rocketchat/provisioning/init.sh create mode 100755 03_container_layer/docker/admin/rocketchat/provisioning/provision-tokens.sh create mode 100755 03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh create mode 100755 03_container_layer/docker/admin/rocketchat/provisioning/provision.sh delete mode 100644 03_container_layer/docker/admin/rocketchat/provisioning/users.yml diff --git a/03_container_layer/docker/admin/rocketchat/.env.example b/03_container_layer/docker/admin/rocketchat/.env.example index 187b496..78c9e37 100644 --- a/03_container_layer/docker/admin/rocketchat/.env.example +++ b/03_container_layer/docker/admin/rocketchat/.env.example @@ -1,5 +1,26 @@ +# ── Network ─────────────────────────────────────────────────────────────────── +RC_HTTP_PORT=3000 +RC_HOSTNAME=localhost + +# Public URL that Rocket.Chat advertises in links and e-mails. RC_BASE_URL=http://localhost:3000 + +# ── Admin credentials ───────────────────────────────────────────────────────── +# The primary admin is created automatically by the official Rocket.Chat image +# via ADMIN_USERNAME / ADMIN_PASS at first boot. RC_ADMIN_USER=rc-admin RC_ADMIN_PASS=Admin1234! RC_ADMIN_EMAIL=admin@range42.local -HTTP_PORT=3000 + +# ── Teams ───────────────────────────────────────────────────────────────────── +# Comma-separated list of player team names. One lead (admin) + N regular users +# are created per entry. +RC_TEAMS=team-blue,team-red +# Name used as the username prefix for instructor accounts. +RC_INSTRUCTOR_ORG=instructors +# Number of instructor accounts to create. +RC_INSTRUCTOR_COUNT=1 +# Number of regular (non-lead) player accounts per team. +RC_USERS_PER_TEAM=2 +# Email domain used for all generated team and instructor accounts. +RC_USER_DOMAIN=range42.local diff --git a/03_container_layer/docker/admin/rocketchat/Dockerfile b/03_container_layer/docker/admin/rocketchat/Dockerfile index e42bfa6..f88c46f 100644 --- a/03_container_layer/docker/admin/rocketchat/Dockerfile +++ b/03_container_layer/docker/admin/rocketchat/Dockerfile @@ -1,32 +1,12 @@ # # ISSUE 147 # - -# -# BUILDER — node:20-alpine (same base as official Rocket.Chat Dockerfile) +# Lightweight provisioner image — Alpine with the REST-API toolchain. +# Provisioning scripts are volume-mounted at runtime (not baked in), +# mirroring the misp-standalone provisioner pattern. # -FROM node:20-alpine AS builder - -RUN apk add --no-cache jq && \ - wget -q https://github.com/mikefarah/yq/releases/download/v4.44.1/yq_linux_amd64 \ - -O /usr/bin/yq && chmod +x /usr/bin/yq - -COPY provisioning/ /provisioning/ -RUN chmod +x /provisioning/init.sh - -# -# RUNTIME — rocketchat/rocket.chat:latest (Alpine/Node-based) -# -FROM rocketchat/rocket.chat:latest AS runtime - -USER root - -# init.sh drives the Rocket.Chat REST API with curl, but the rocket.chat image -# (Alpine) ships neither curl nor wget — add curl so provisioning can run. -RUN apk add --no-cache curl +FROM alpine:3.20 -COPY --from=builder /usr/bin/yq /usr/bin/yq -COPY --from=builder /usr/bin/jq /usr/bin/jq -COPY --from=builder /provisioning/ /provisioning/ +RUN apk add --no-cache bash curl jq openssl -ENTRYPOINT ["/provisioning/init.sh"] +ENTRYPOINT ["/provisioning/provision.sh"] diff --git a/03_container_layer/docker/admin/rocketchat/Makefile b/03_container_layer/docker/admin/rocketchat/Makefile index f1c3063..03bd0fd 100644 --- a/03_container_layer/docker/admin/rocketchat/Makefile +++ b/03_container_layer/docker/admin/rocketchat/Makefile @@ -6,7 +6,7 @@ SERVICE = rocketchat PROVISIONER = rocketchat-provisioner DEBUG_SERVICE = $(SERVICE)-debug -.PHONY: help up down stop stop-debug-build build rebuild build-up rebuild-up tokens reprovision term term-debug-build clean print +.PHONY: help up down stop stop-debug-build build rebuild build-up rebuild-up tokens keys reprovision term term-debug-build clean print help: @echo "" @@ -24,7 +24,8 @@ help: @echo " make rebuild - full rebuild $(SERVICE) without cache" @echo " make rebuild-up - full rebuild $(SERVICE) without cache then run" @echo "" - @echo " make tokens - print generated personal access tokens" + @echo " make tokens - print generated personal access tokens (username:PAT)" + @echo " make keys - print full credentials JSON (all users + passwords)" @echo " make term - open shell in $(SERVICE) container" @echo " make term-debug-build - build and get term on $(DEBUG_SERVICE) " @echo " make clean - delete all containers, images, volumes and unused network" @@ -70,6 +71,9 @@ rebuild-up: tokens: docker run --rm -v rocketchat-tokens:/tokens alpine cat /tokens/tokens.txt +keys: + docker run --rm -v rocketchat-tokens:/tokens alpine cat /tokens/rc-credentials.json + reprovision: docker run --rm -v rocketchat-tokens:/tokens alpine rm -f /tokens/.provisioned docker compose up $(PROVISIONER) diff --git a/03_container_layer/docker/admin/rocketchat/README.md b/03_container_layer/docker/admin/rocketchat/README.md index 40bcec1..a678678 100644 --- a/03_container_layer/docker/admin/rocketchat/README.md +++ b/03_container_layer/docker/admin/rocketchat/README.md @@ -1,6 +1,6 @@ # Rocket.Chat — Standalone Docker Deployment -Issue 147. Dockerized Rocket.Chat with MongoDB replica set and automated user / personal-access-token provisioning. +Issue 147. Dockerized Rocket.Chat with MongoDB replica set and team-aware automated user and Personal Access Token provisioning. --- @@ -24,8 +24,9 @@ $EDITOR .env # 2. Build and start the full stack make build-up -# 3. Wait for the provisioner to finish, then check tokens -make tokens +# 3. Wait for the provisioner to finish (~2 min on first boot), then check tokens +make tokens # username:PAT pairs +make keys # full credentials JSON ``` > **MongoDB replica set** initialises automatically via the `mongo-init-replica` one-shot container. No manual `rs.initiate()` step is needed. @@ -35,83 +36,124 @@ Default admin credentials: `rc-admin` / `Admin1234!` (change in `.env`). --- -## Build & Push - -```sh -# Build provisioner image -make build +## Provisioning Architecture -# Full rebuild without cache -make rebuild +User provisioning is handled by a dedicated one-shot `provisioner` service (Alpine + bash/curl/jq/openssl). Scripts are **volume-mounted at runtime** — not baked into the image — so they can be updated without a rebuild. -# Tag and push (adjust registry as needed) -docker tag rocketchat-provisioner registry.example.com/range42/rocketchat-provisioner:latest -docker push registry.example.com/range42/rocketchat-provisioner:latest +``` +provision.sh ← orchestrator, mounted as ENTRYPOINT + └─ provision-users.sh creates all user accounts, writes /tokens/rc-credentials.json + └─ provision-tokens.sh generates PATs for every user, writes /tokens/tokens.txt ``` ---- - -## Declaring Users - -Edit `provisioning/users.yml` before first deployment: +Provisioning is **idempotent**: a `/tokens/.provisioned` stamp prevents re-running on container restart. To re-provision, run `make reprovision`. -```yaml -# !! CHANGE ALL PASSWORDS BEFORE DEPLOYING !! +### What gets created -admins: - - username: rc-admin2 - email: admin2@range42.local - password: "Admin1234!" - name: "RC Admin 2" +Given the default `.env.example` values (`RC_TEAMS=team-blue,team-red`, `RC_INSTRUCTOR_COUNT=1`, `RC_USERS_PER_TEAM=2`): -users: - - username: trainee01 - email: trainee01@range42.local - password: "Trainee1234!" - name: "Trainee 01" -``` +| Username | Role | Team | +|---|---|---| +| `rc-admin` | admin | admin | +| `instructors` | admin | instructors | +| `team-blue-lead` | admin | team-blue | +| `team-blue-user1` | user | team-blue | +| `team-blue-user2` | user | team-blue | +| `team-red-lead` | admin | team-red | +| `team-red-user1` | user | team-red | +| `team-red-user2` | user | team-red | -The primary admin (`rc-admin`) is created automatically via the `ADMIN_USERNAME` environment variable. The users listed in `users.yml` are **additional** accounts. +All passwords are auto-generated (20-char, `R42!` prefix + `openssl rand`). Retrieve them via `make keys`. --- ## Token Retrieval -Personal access tokens are written to `/tokens/tokens.txt` (inside the `rocketchat-tokens` volume) at the end of provisioning. +Two output files are written to the `rocketchat-tokens` named volume: ```sh -# Print all tokens +# Personal Access Tokens (username:PAT, one per line) make tokens -# Or read directly from the volume -docker run --rm -v rocketchat_rocketchat-tokens:/tokens:ro busybox cat /tokens/tokens.txt +# Full credentials JSON (usernames, passwords, roles, teams) +make keys ``` -Each line has the format: +`tokens.txt` format: +``` +rc-admin:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +team-blue-lead:yyyyyyyyyyyyyyyyyyyyyyyyyyyy +``` +`rc-credentials.json` format: +```json +{ + "service": "rocketchat", + "baseurl": "http://localhost:3000", + "users": [ + {"username": "rc-admin", "role": "admin", "team": "admin", "password": "Admin1234!"}, + {"username": "team-blue-lead", "role": "admin", "team": "team-blue", "password": "R42!..."}, + {"username": "team-blue-user1","role": "user", "team": "team-blue", "password": "R42!..."} + ] +} ``` -username:personalAccessToken + +Both files are `chmod 600` inside the volume. + +--- + +## Build & Push + +```sh +make build # build provisioner image +make rebuild # full rebuild without cache + +# Tag and push (adjust registry as needed) +docker tag rocketchat-provisioner registry.example.com/range42/rocketchat-provisioner:latest +docker push registry.example.com/range42/rocketchat-provisioner:latest ``` --- +## Environment Variables + +| Variable | Default | Description | +|---|---|---| +| `RC_HTTP_PORT` | `3000` | Host port mapped to Rocket.Chat | +| `RC_HOSTNAME` | `localhost` | Hostname for URL construction | +| `RC_BASE_URL` | `http://localhost:3000` | Public URL (`ROOT_URL` in Rocket.Chat) | +| `RC_ADMIN_USER` | `rc-admin` | Bootstrap admin username | +| `RC_ADMIN_PASS` | `Admin1234!` | Bootstrap admin password | +| `RC_ADMIN_EMAIL` | `admin@range42.local` | Bootstrap admin email | +| `RC_TEAMS` | `team-blue,team-red` | Comma-separated team names | +| `RC_INSTRUCTOR_ORG` | `instructors` | Username prefix for instructor accounts | +| `RC_INSTRUCTOR_COUNT` | `1` | Number of instructor accounts to create | +| `RC_USERS_PER_TEAM` | `2` | Regular (non-lead) users per team | +| `RC_USER_DOMAIN` | `range42.local` | Email domain for generated accounts | + +--- + ## API Usage Examples ```sh # Get server info (no auth needed) curl http://localhost:3000/api/v1/info -# List channels (authenticated) -TOKEN="" -USER_ID="" +# Authenticate and store credentials +LOGIN=$(curl -sf -X POST http://localhost:3000/api/v1/login \ + -H "Content-Type: application/json" \ + -d '{"username":"rc-admin","password":"Admin1234!"}') +USER_ID=$(echo "${LOGIN}" | jq -r '.data.userId') +AUTH_TOKEN=$(echo "${LOGIN}" | jq -r '.data.authToken') -curl -X GET http://localhost:3000/api/v1/channels.list \ - -H "X-Auth-Token: ${TOKEN}" \ +# List channels +curl http://localhost:3000/api/v1/channels.list \ + -H "X-Auth-Token: ${AUTH_TOKEN}" \ -H "X-User-Id: ${USER_ID}" # Post a message curl -X POST http://localhost:3000/api/v1/chat.postMessage \ - -H "X-Auth-Token: ${TOKEN}" \ + -H "X-Auth-Token: ${AUTH_TOKEN}" \ -H "X-User-Id: ${USER_ID}" \ -H "Content-Type: application/json" \ -d '{"channel":"#general","text":"Hello from range42!"}' @@ -119,29 +161,14 @@ curl -X POST http://localhost:3000/api/v1/chat.postMessage \ --- -## Environment Variables - -| Variable | Default | Description | -|---|---|---| -| `RC_BASE_URL` | `http://localhost:3000` | Public URL (used by Rocket.Chat as `ROOT_URL`) | -| `RC_ADMIN_USER` | `rc-admin` | Initial admin username | -| `RC_ADMIN_PASS` | `Admin1234!` | Initial admin password | -| `RC_ADMIN_EMAIL` | `admin@range42.local` | Initial admin email | -| `HTTP_PORT` | `3000` | Host port mapped to Rocket.Chat | - ---- - ## Troubleshooting ### MongoDB replica set issues **Symptom:** Rocket.Chat exits with `MongoServerError: not primary`. -**Cause:** The replica set has not been initialised yet. The `mongo-init-replica` container handles this automatically, but it requires MongoDB to be healthy first. - **Fix:** ```sh -# Check mongo-init-replica logs docker logs rocketchat-mongo-init # Force re-init manually if needed @@ -151,19 +178,24 @@ docker exec rocketchat-mongodb mongosh --eval \ ### Provisioner exits with auth error -**Symptom:** `[init] ERROR: Failed to authenticate as admin.` +**Symptom:** `[provision-users] ERROR: Failed to authenticate as rc-admin.` -**Cause:** Rocket.Chat is not yet fully ready (it can take 60–90 s on first boot). +**Cause:** Rocket.Chat is not yet fully ready (can take 60–90 s on first boot). The provisioner waits up to 180 s then exits with an error. -**Fix:** The provisioner will be restarted automatically by Docker Compose if `restart: "no"` is overridden, or you can re-run it manually: +**Fix:** Re-run the provisioner: ```sh -docker compose run --rm provisioner +make reprovision ``` -### Tokens already exist +### Re-provisioning -If the provisioner runs a second time, `generatePersonalAccessToken` will fail for tokens named `api-token` that already exist. This is handled gracefully — a warning is printed and the existing token is left in place. Re-provision with a clean volume to regenerate: +Remove the idempotency stamp and re-run: ```sh -docker volume rm rocketchat_rocketchat-tokens -docker compose run --rm provisioner +make reprovision +``` + +To completely reset users and tokens (start fresh): +```sh +docker compose down -v +make build-up ``` diff --git a/03_container_layer/docker/admin/rocketchat/compose.yml b/03_container_layer/docker/admin/rocketchat/compose.yml index b17ac3a..d751419 100644 --- a/03_container_layer/docker/admin/rocketchat/compose.yml +++ b/03_container_layer/docker/admin/rocketchat/compose.yml @@ -86,13 +86,14 @@ services: context: . dockerfile: Dockerfile container_name: rocketchat-provisioner + env_file: .env environment: - RC_URL: http://rocketchat:3000 - RC_ADMIN_USER: ${RC_ADMIN_USER:-rc-admin} - RC_ADMIN_PASS: ${RC_ADMIN_PASS:-Admin1234!} - USERS_FILE: /provisioning/users.yml + RC_URL: http://rocketchat:3000 volumes: - rocketchat-tokens:/tokens + - ./provisioning/provision.sh:/provisioning/provision.sh:ro + - ./provisioning/provision-users.sh:/provisioning/provision-users.sh:ro + - ./provisioning/provision-tokens.sh:/provisioning/provision-tokens.sh:ro depends_on: rocketchat: condition: service_healthy diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/init.sh b/03_container_layer/docker/admin/rocketchat/provisioning/init.sh deleted file mode 100644 index 0dc2e1c..0000000 --- a/03_container_layer/docker/admin/rocketchat/provisioning/init.sh +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env sh -# -# ISSUE 147 -# -# Rocket.Chat provisioner: creates users and personal access tokens. -# Reads: $USERS_FILE (YAML with admins[] and users[] lists) -# Writes: /tokens/tokens.txt (username:token per line) -# - -set -eu - -RC_URL="${RC_URL:-http://rocketchat:3000}" -RC_ADMIN_USER="${RC_ADMIN_USER:-rc-admin}" -RC_ADMIN_PASS="${RC_ADMIN_PASS:-Admin1234!}" -USERS_FILE="${USERS_FILE:-/provisioning/users.yml}" -TOKENS_FILE="/tokens/tokens.txt" -STAMP_FILE="/tokens/.provisioned" - -# ───────────────────────────────────────────────────────────────────────────── -# 1. Wait for Rocket.Chat health (max 180s, 60 × 3s) -# ───────────────────────────────────────────────────────────────────────────── -echo "[init] Waiting for Rocket.Chat at ${RC_URL} ..." -attempts=0 -max_attempts=60 - -until curl -sf "${RC_URL}/api/info" >/dev/null 2>&1; do - attempts=$((attempts + 1)) - if [ "${attempts}" -ge "${max_attempts}" ]; then - echo "[init] ERROR: Rocket.Chat did not become healthy after $((max_attempts * 3))s. Aborting." - exit 1 - fi - echo "[init] Waiting ... (${attempts}/${max_attempts})" - sleep 3 -done - -echo "[init] Rocket.Chat is up." - -# ───────────────────────────────────────────────────────────────────────────── -# 2. Idempotency stamp -# ───────────────────────────────────────────────────────────────────────────── -if [ -f "${STAMP_FILE}" ]; then - echo "[init] Already provisioned (${STAMP_FILE} exists). Exiting." - exit 0 -fi - -mkdir -p /tokens -: > "${TOKENS_FILE}" - -# ───────────────────────────────────────────────────────────────────────────── -# 3. Login as admin — capture auth token and user ID -# ───────────────────────────────────────────────────────────────────────────── -echo "[init] Logging in as ${RC_ADMIN_USER} ..." -auth_resp=$(curl -sf -X POST "${RC_URL}/api/v1/login" \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg u "${RC_ADMIN_USER}" --arg p "${RC_ADMIN_PASS}" \ - '{"username":$u,"password":$p}')") - -rc_admin_token=$(echo "${auth_resp}" | jq -r '.data.authToken') -rc_admin_id=$(echo "${auth_resp}" | jq -r '.data.userId') - -if [ -z "${rc_admin_token}" ] || [ "${rc_admin_token}" = "null" ]; then - echo "[init] ERROR: Failed to authenticate as admin. Check RC_ADMIN_USER / RC_ADMIN_PASS." - exit 1 -fi - -echo "[init] Admin auth OK (userId=${rc_admin_id})." - -# ───────────────────────────────────────────────────────────────────────────── -# Helper: create a user via REST API -# Usage: create_user -# ───────────────────────────────────────────────────────────────────────────── -create_user() { - _username="$1" - _email="$2" - _password="$3" - _name="$4" - _roles="$5" - - echo "[init] Creating user: ${_username} ..." - _payload=$(jq -n \ - --arg u "${_username}" \ - --arg e "${_email}" \ - --arg p "${_password}" \ - --arg n "${_name}" \ - --argjson r "${_roles}" \ - '{"username":$u,"email":$e,"password":$p,"name":$n, - "roles":$r,"joinDefaultChannels":true, - "sendWelcomeEmail":false,"verified":true}') - - _resp=$(curl -sf -X POST "${RC_URL}/api/v1/users.create" \ - -H "X-Auth-Token: ${rc_admin_token}" \ - -H "X-User-Id: ${rc_admin_id}" \ - -H "Content-Type: application/json" \ - -d "${_payload}" 2>&1) || true - - # tolerate "Username is already in use" (idempotent) - _success=$(echo "${_resp}" | jq -r '.success // false') - _error=$(echo "${_resp}" | jq -r '.error // ""') - - if [ "${_success}" = "true" ]; then - echo "[init] User ${_username} created." - elif echo "${_error}" | grep -qi "already in use\|already exists\|duplicate"; then - echo "[init] User ${_username} already exists — skipping." - else - echo "[init] WARNING: Unexpected response for ${_username}: ${_resp}" - fi -} - -# ───────────────────────────────────────────────────────────────────────────── -# Helper: generate personal access token for a user -# The user must authenticate as themselves (tokens are user-owned in RC). -# Usage: generate_token -# ───────────────────────────────────────────────────────────────────────────── -generate_token() { - _username="$1" - _password="$2" - - echo "[token] Generating PAT for ${_username} ..." - - # Login as the user - _user_auth=$(curl -sf -X POST "${RC_URL}/api/v1/login" \ - -H "Content-Type: application/json" \ - -d "$(jq -n --arg u "${_username}" --arg p "${_password}" \ - '{"username":$u,"password":$p}')") || true - - _user_token=$(echo "${_user_auth}" | jq -r '.data.authToken // ""') - _user_id=$(echo "${_user_auth}" | jq -r '.data.userId // ""') - - if [ -z "${_user_token}" ] || [ "${_user_token}" = "null" ]; then - echo "[token] WARNING: Could not log in as ${_username} — skipping token generation." - return - fi - - # Generate personal access token (tokenName must be unique per user) - _token_resp=$(curl -sf -X POST "${RC_URL}/api/v1/users.generatePersonalAccessToken" \ - -H "X-Auth-Token: ${_user_token}" \ - -H "X-User-Id: ${_user_id}" \ - -H "Content-Type: application/json" \ - -d '{"tokenName":"api-token"}') || true - - _pat=$(echo "${_token_resp}" | jq -r '.token // "ERROR"') - - if [ "${_pat}" = "ERROR" ] || [ -z "${_pat}" ]; then - # May already exist — try to list and skip gracefully - echo "[token] WARNING: Could not generate PAT for ${_username} (may already exist)." - return - fi - - echo "[init] + token generated for ${_username}" - echo "${_username}:${_pat}" >> "${TOKENS_FILE}" -} - -# ───────────────────────────────────────────────────────────────────────────── -# 4. Create admin users -# ───────────────────────────────────────────────────────────────────────────── -echo "[init] --- Processing admins ---" -admin_count=$(yq '.admins | length' "${USERS_FILE}") -i=0 -while [ "${i}" -lt "${admin_count}" ]; do - username=$(yq ".admins[${i}].username" "${USERS_FILE}") - email=$(yq ".admins[${i}].email" "${USERS_FILE}") - password=$(yq ".admins[${i}].password" "${USERS_FILE}") - name=$(yq ".admins[${i}].name" "${USERS_FILE}") - - create_user "${username}" "${email}" "${password}" "${name}" '["admin"]' - i=$((i + 1)) -done - -# ───────────────────────────────────────────────────────────────────────────── -# 5. Create regular users -# ───────────────────────────────────────────────────────────────────────────── -echo "[init] --- Processing users ---" -user_count=$(yq '.users | length' "${USERS_FILE}") -i=0 -while [ "${i}" -lt "${user_count}" ]; do - username=$(yq ".users[${i}].username" "${USERS_FILE}") - email=$(yq ".users[${i}].email" "${USERS_FILE}") - password=$(yq ".users[${i}].password" "${USERS_FILE}") - name=$(yq ".users[${i}].name" "${USERS_FILE}") - - create_user "${username}" "${email}" "${password}" "${name}" '["user"]' - i=$((i + 1)) -done - -# ───────────────────────────────────────────────────────────────────────────── -# 6. Generate personal access tokens for ALL users (admins + regular) -# ───────────────────────────────────────────────────────────────────────────── -echo "[init] --- Generating personal access tokens ---" - -# Tokens for admin users -i=0 -while [ "${i}" -lt "${admin_count}" ]; do - username=$(yq ".admins[${i}].username" "${USERS_FILE}") - password=$(yq ".admins[${i}].password" "${USERS_FILE}") - generate_token "${username}" "${password}" - i=$((i + 1)) -done - -# Tokens for regular users -i=0 -while [ "${i}" -lt "${user_count}" ]; do - username=$(yq ".users[${i}].username" "${USERS_FILE}") - password=$(yq ".users[${i}].password" "${USERS_FILE}") - generate_token "${username}" "${password}" - i=$((i + 1)) -done - -# ───────────────────────────────────────────────────────────────────────────── -# 7. Print summary and mark as provisioned -# ───────────────────────────────────────────────────────────────────────────── -echo "" -echo "[init] ──────────────────────────────────────" -echo "[init] Provisioning complete." -echo "[init] Tokens written to: ${TOKENS_FILE}" -echo "[init] ──────────────────────────────────────" -if [ -s "${TOKENS_FILE}" ]; then - echo "[init] Token file: /tokens/tokens.txt — use 'make tokens' to retrieve." -fi - -touch "${STAMP_FILE}" -echo "[init] Done." diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/provision-tokens.sh b/03_container_layer/docker/admin/rocketchat/provisioning/provision-tokens.sh new file mode 100755 index 0000000..fafbd7f --- /dev/null +++ b/03_container_layer/docker/admin/rocketchat/provisioning/provision-tokens.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# provision-tokens.sh — generates a Personal Access Token for every provisioned user. +# Called by provision.sh after provision-users.sh completes. +# +# Reads: +# /tokens/rc-credentials.json — written by provision-users.sh +# RC_URL — RC internal URL (default: http://rocketchat:3000) +# +# Writes: +# /tokens/tokens.txt — one "username:PAT" line per user, chmod 600 +set -euo pipefail + +RC_URL="${RC_URL:-http://rocketchat:3000}" + +CREDS_FILE="/tokens/rc-credentials.json" +TOKENS_FILE="/tokens/tokens.txt" + +log() { echo "[provision-tokens] $*" >&2; } +fail() { echo "[provision-tokens] ERROR: $*" >&2; exit 1; } + +[ -f "${CREDS_FILE}" ] || fail "${CREDS_FILE} not found — did provision-users.sh run?" + +: > "${TOKENS_FILE}" +chmod 600 "${TOKENS_FILE}" + +# ── Generate a PAT for one user ─────────────────────────────────────────────── + +generate_token() { + local username="$1" password="$2" + + # Login as the user to get their own auth token + local auth_resp login_token user_id + auth_resp=$(curl -sf -X POST "${RC_URL}/api/v1/login" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg u "${username}" --arg p "${password}" '{"username":$u,"password":$p}')") || { + log "WARNING: Login failed for ${username} — skipping token." + return 0 + } + + login_token=$(echo "${auth_resp}" | jq -r '.data.authToken // empty') + user_id=$(echo "${auth_resp}" | jq -r '.data.userId // empty') + + if [ -z "${login_token}" ] || [ -z "${user_id}" ]; then + log "WARNING: Could not parse auth response for ${username} — skipping." + return 0 + fi + + # Generate a named Personal Access Token + local token_resp pat + token_resp=$(curl -sf -X POST "${RC_URL}/api/v1/users.generatePersonalAccessToken" \ + -H "X-Auth-Token: ${login_token}" \ + -H "X-User-Id: ${user_id}" \ + -H "Content-Type: application/json" \ + -d '{"tokenName":"api-token"}') || { + log "WARNING: PAT generation failed for ${username} — skipping." + return 0 + } + + pat=$(echo "${token_resp}" | jq -r '.token // empty') + if [ -z "${pat}" ]; then + log "WARNING: Empty PAT for ${username}: ${token_resp}" + return 0 + fi + + printf '%s:%s\n' "${username}" "${pat}" >> "${TOKENS_FILE}" + log "Token generated for ${username}." +} + +# ── Iterate over all users in rc-credentials.json ──────────────────────────── + +log "Generating Personal Access Tokens …" + +while IFS= read -r entry; do + username=$(echo "${entry}" | jq -r '.username') + password=$(echo "${entry}" | jq -r '.password') + generate_token "${username}" "${password}" +done < <(jq -c '.users[]' "${CREDS_FILE}") + +log "Tokens written to ${TOKENS_FILE}" diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh b/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh new file mode 100755 index 0000000..8f3841f --- /dev/null +++ b/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env bash +# provision-users.sh — creates all Rocket.Chat user accounts. +# Called by provision.sh after the rocketchat service is healthy. +# +# Reads env vars: +# RC_URL — RC internal URL (default: http://rocketchat:3000) +# RC_ADMIN_USER — bootstrap admin name (default: rc-admin) +# RC_ADMIN_PASS — bootstrap admin pass (default: Admin1234!) +# RC_TEAMS — comma-separated team names (default: team-blue,team-red) +# RC_INSTRUCTOR_ORG — instructor prefix (default: instructors) +# RC_INSTRUCTOR_COUNT — instructor accounts (default: 1) +# RC_USERS_PER_TEAM — regular users/team (default: 2) +# RC_USER_DOMAIN — email domain (default: range42.local) +# +# Writes: +# /tokens/rc-credentials.json — structured credentials for all accounts +# /tokens/.provisioned — idempotency stamp +set -euo pipefail + +RC_URL="${RC_URL:-http://rocketchat:3000}" +RC_ADMIN_USER="${RC_ADMIN_USER:-rc-admin}" +RC_ADMIN_PASS="${RC_ADMIN_PASS:-Admin1234!}" +RC_TEAMS="${RC_TEAMS:-team-blue,team-red}" +RC_INSTRUCTOR_ORG="${RC_INSTRUCTOR_ORG:-instructors}" +RC_INSTRUCTOR_COUNT="${RC_INSTRUCTOR_COUNT:-1}" +RC_USERS_PER_TEAM="${RC_USERS_PER_TEAM:-2}" +RC_USER_DOMAIN="${RC_USER_DOMAIN:-range42.local}" + +CREDS_FILE="/tokens/rc-credentials.json" +CREDS_TMP="/tokens/.creds.tmp" +STAMP_FILE="/tokens/.provisioned" + +log() { echo "[provision-users] $*" >&2; } +fail() { echo "[provision-users] ERROR: $*" >&2; exit 1; } + +# ── Idempotency guard ───────────────────────────────────────────────────────── + +if [ -f "${STAMP_FILE}" ]; then + log "Already provisioned (${STAMP_FILE} exists). Exiting." + exit 0 +fi + +mkdir -p /tokens + +# ── Wait for Rocket.Chat API ────────────────────────────────────────────────── + +log "Waiting for Rocket.Chat at ${RC_URL} …" +attempts=0 +until curl -sf "${RC_URL}/api/info" >/dev/null 2>&1; do + attempts=$((attempts + 1)) + [ "${attempts}" -ge 60 ] && fail "Rocket.Chat did not respond after 180 s." + log "Waiting … (${attempts}/60)" + sleep 3 +done +log "Rocket.Chat is up." + +# ── Login as bootstrap admin ────────────────────────────────────────────────── + +auth_resp=$(curl -sf -X POST "${RC_URL}/api/v1/login" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg u "${RC_ADMIN_USER}" --arg p "${RC_ADMIN_PASS}" '{"username":$u,"password":$p}')") + +ADMIN_TOKEN=$(echo "${auth_resp}" | jq -r '.data.authToken') +ADMIN_ID=$(echo "${auth_resp}" | jq -r '.data.userId') + +[ -n "${ADMIN_TOKEN}" ] && [ "${ADMIN_TOKEN}" != "null" ] || \ + fail "Failed to authenticate as ${RC_ADMIN_USER}. Check RC_ADMIN_USER / RC_ADMIN_PASS." + +log "Admin auth OK (userId=${ADMIN_ID})." + +# ── Password generator ──────────────────────────────────────────────────────── +# Produces a 20-char password satisfying complexity requirements (R42! prefix). + +gen_password() { + printf 'R42!%s' "$(openssl rand -base64 16 | tr -d '/+=')" | head -c 20 +} + +# ── REST: create a user ─────────────────────────────────────────────────────── + +create_user() { + local username="$1" email="$2" password="$3" name="$4" roles="$5" + local payload resp success error + payload=$(jq -n \ + --arg u "${username}" --arg e "${email}" \ + --arg p "${password}" --arg n "${name}" \ + --argjson r "${roles}" \ + '{"username":$u,"email":$e,"password":$p,"name":$n, + "roles":$r,"joinDefaultChannels":true, + "sendWelcomeEmail":false,"verified":true}') + resp=$(curl -sf -X POST "${RC_URL}/api/v1/users.create" \ + -H "X-Auth-Token: ${ADMIN_TOKEN}" \ + -H "X-User-Id: ${ADMIN_ID}" \ + -H "Content-Type: application/json" \ + -d "${payload}" 2>&1) || true + success=$(echo "${resp}" | jq -r '.success // false') + error=$(echo "${resp}" | jq -r '.error // ""') + if [ "${success}" = "true" ]; then + log "User ${username} created." + elif echo "${error}" | grep -qi "already in use\|already exists\|duplicate"; then + log "User ${username} already exists — skipping." + else + log "WARNING: Unexpected response for ${username}: ${resp}" + fi +} + +# ── Credentials accumulator ─────────────────────────────────────────────────── + +printf '[\n' > "${CREDS_TMP}" +_CRED_FIRST=true + +append_cred() { + local username="$1" role="$2" team="$3" password="$4" + "${_CRED_FIRST}" || printf ',\n' >> "${CREDS_TMP}" + _CRED_FIRST=false + jq -n \ + --arg u "${username}" --arg r "${role}" \ + --arg t "${team}" --arg p "${password}" \ + '{"username":$u,"role":$r,"team":$t,"password":$p}' >> "${CREDS_TMP}" +} + +# ── Primary admin ───────────────────────────────────────────────────────────── + +log "Recording primary admin ${RC_ADMIN_USER} …" +append_cred "${RC_ADMIN_USER}" "admin" "admin" "${RC_ADMIN_PASS}" + +# ── Instructors ─────────────────────────────────────────────────────────────── + +log "Creating ${RC_INSTRUCTOR_COUNT} instructor account(s) …" +for i in $(seq 1 "${RC_INSTRUCTOR_COUNT}"); do + suffix=$([ "${RC_INSTRUCTOR_COUNT}" -eq 1 ] && echo "" || echo "${i}") + username="${RC_INSTRUCTOR_ORG}${suffix}" + email="${username}@${RC_USER_DOMAIN}" + password=$(gen_password) + name="Instructor${suffix:+ ${suffix}}" + create_user "${username}" "${email}" "${password}" "${name}" '["admin"]' + append_cred "${username}" "admin" "${RC_INSTRUCTOR_ORG}" "${password}" +done + +# ── Team users ──────────────────────────────────────────────────────────────── + +IFS=',' read -ra TEAM_LIST <<< "${RC_TEAMS}" +for raw_team in "${TEAM_LIST[@]}"; do + team=$(echo "${raw_team}" | tr -d '[:space:]') + [ -z "${team}" ] && continue + + log "Creating team '${team}' (1 lead + ${RC_USERS_PER_TEAM} user(s)) …" + + # Team lead — admin role so they can manage their own team channel + lead_pass=$(gen_password) + create_user "${team}-lead" "${team}-lead@${RC_USER_DOMAIN}" "${lead_pass}" "${team} Lead" '["admin"]' + append_cred "${team}-lead" "admin" "${team}" "${lead_pass}" + + # Regular team members + for i in $(seq 1 "${RC_USERS_PER_TEAM}"); do + user_pass=$(gen_password) + create_user "${team}-user${i}" "${team}-user${i}@${RC_USER_DOMAIN}" \ + "${user_pass}" "${team} User ${i}" '["user"]' + append_cred "${team}-user${i}" "user" "${team}" "${user_pass}" + done +done + +# ── Write rc-credentials.json ───────────────────────────────────────────────── + +printf '\n]\n' >> "${CREDS_TMP}" +jq \ + --arg svc "rocketchat" \ + --arg url "${RC_BASE_URL:-http://localhost:3000}" \ + '{"service":$svc,"baseurl":$url,"users":.}' \ + "${CREDS_TMP}" > "${CREDS_FILE}" +chmod 600 "${CREDS_FILE}" +rm -f "${CREDS_TMP}" + +log "Credentials written to ${CREDS_FILE}" +touch "${STAMP_FILE}" +log "User provisioning complete." diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/provision.sh b/03_container_layer/docker/admin/rocketchat/provisioning/provision.sh new file mode 100755 index 0000000..85135c2 --- /dev/null +++ b/03_container_layer/docker/admin/rocketchat/provisioning/provision.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# provision.sh — orchestrates all provisioning steps in dependency order. +# Entrypoint for the provisioner service in compose.yml. +set -euo pipefail + +/provisioning/provision-users.sh +/provisioning/provision-tokens.sh diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/users.yml b/03_container_layer/docker/admin/rocketchat/provisioning/users.yml deleted file mode 100644 index f624711..0000000 --- a/03_container_layer/docker/admin/rocketchat/provisioning/users.yml +++ /dev/null @@ -1,22 +0,0 @@ -# !! CHANGE ALL PASSWORDS BEFORE DEPLOYING !! -# Personal access tokens are auto-generated and written to /tokens/tokens.txt - -admins: - - username: rc-admin2 - email: admin2@range42.local - password: "Admin1234!" - name: "RC Admin 2" - -users: - - username: trainee01 - email: trainee01@range42.local - password: "Trainee1234!" - name: "Trainee 01" - - username: trainee02 - email: trainee02@range42.local - password: "Trainee1234!" - name: "Trainee 02" - - username: trainee03 - email: trainee03@range42.local - password: "Trainee1234!" - name: "Trainee 03" From d301aa57f0c8a541153240b80d12bdfd61f7ff64 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Fri, 12 Jun 2026 09:43:25 +0200 Subject: [PATCH 4/5] chore: revert 02_ansible_layer and 05_topology_layer to match dev Brings the branch back in sync with dev for these two layers: - Replace debian-based docker tasks with ubuntu equivalents - Remove software.install.rocketchat Ansible role (not present on dev) - Remove admin-rocketchat box template (not present on dev) --- .../tasks/main.yml | 8 +- .../tasks/{debian-based => ubuntu}/deploy.yml | 8 -- .../software.install.rocketchat/README.md | 36 --------- .../defaults/main.yml | 27 ------- .../software.install.rocketchat/meta/main.yml | 14 ---- .../tasks/main.yml | 23 ------ .../tasks/include/docker/_main.yaml | 12 +-- .../include/docker/debian-based/docker.yaml | 80 ------------------- .../docker/debian-based/docker_compose.yaml | 58 -------------- .../tasks/include/docker/ubuntu/docker.yaml | 66 +++++++++++++++ .../include/docker/ubuntu/docker_compose.yaml | 57 +++++++++++++ .../admin-rocketchat/v1.0.0/template.yml | 30 ------- 12 files changed, 132 insertions(+), 287 deletions(-) rename 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/{debian-based => ubuntu}/deploy.yml (84%) delete mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/README.md delete mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml delete mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml delete mode 100644 02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml delete mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml delete mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml create mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml create mode 100644 02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml delete mode 100644 05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml diff --git a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml index 54bb4d2..925d39c 100644 --- a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml +++ b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/main.yml @@ -7,10 +7,8 @@ ansible.builtin.setup: # -# Tasks in ./debian-based/deploy.yml are apt/rsync based, so they run on every -# Debian-family host (Ubuntu + Debian). -- name: INCLUDE - deploy tasks - debian-based - ansible.builtin.include_tasks: ./debian-based/deploy.yml - when: ansible_facts['distribution'] in ['Ubuntu', 'Debian'] +- name: INCLUDE - ufw tasks - ubuntu + ansible.builtin.include_tasks: ./ubuntu/deploy.yml + when: ansible_facts['distribution'] == "Ubuntu" #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### diff --git a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/ubuntu/deploy.yml similarity index 84% rename from 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml rename to 02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/ubuntu/deploy.yml index 622f2cd..9c5dcfb 100644 --- a/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/debian-based/deploy.yml +++ b/02_ansible_layer/admin/roles/software.configure.docker-compose/tasks/ubuntu/deploy.yml @@ -51,18 +51,10 @@ block: # # - # Retry on transient failures (intermittent registry/mirror anycast timeouts - # pulling images are common on egress-filtered ranges). `up` is idempotent, - # so re-running resumes the pull. A persistent failure still surfaces after - # the retries are exhausted. - name: DOCKER - DOCKER-COMPOSE - RUN community.docker.docker_compose_v2: project_src: "{{ REMOTE_PROJECT_DIR }}" state: present - register: compose_run - until: compose_run is succeeded - retries: 5 - delay: 20 # - name: DOCKER - GET CONTAINER INFO {{ LABEL_PROJET_NAME }} community.docker.docker_container_info: diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/README.md b/02_ansible_layer/admin/roles/software.install.rocketchat/README.md deleted file mode 100644 index 287e235..0000000 --- a/02_ansible_layer/admin/roles/software.install.rocketchat/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# software.install.rocketchat - -Deploys the **Rocket.Chat** docker-compose stack onto a target box. - -The stack itself (Rocket.Chat + MongoDB replica set + token provisioner) lives in -the catalog at [`03_container_layer/docker/admin/rocketchat/`](../../../../03_container_layer/docker/admin/rocketchat/). -This role rsyncs that directory onto the box and brings it up, delegating the -deploy + `docker compose up` to the shared -[`software.configure.docker-compose`](../software.configure.docker-compose/) role. - -## Requirements - -Docker engine + compose plugin must already be installed on the target. Wire -`software.install.warmup.basic_packages` with `INSTALL_PACKAGES_DOCKER: "YES"` -and `INSTALL_PACKAGES_DOCKER_COMPOSE: "YES"` before this role (the -`admin-rocketchat` box template does this). - -`RANGE42_INVENTORY` must be exported on the controller (done by `range42-context`) -so the stack source resolves. - -## Role variables - -| Variable | Default | Purpose | -|----------|---------|---------| -| `ROCKETCHAT_LOCAL_PROJECT_DIR` | `{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/rocketchat` | Controller-side stack source | -| `ROCKETCHAT_REMOTE_PROJECT_DIR` | `/opt/range42/rocketchat` | Where the stack is staged + run on the box | -| `ROCKETCHAT_OPERATOR_USER` | `{{ default_admin_vm_ci_user }}` | Owner of staged files (scenario cloud-init admin user) | -| `ROCKETCHAT_CONTAINER_NAME` | `rocketchat` | Main container polled after `up` | -| `ROCKETCHAT_LABEL_PROJECT_TYPE` | `admin` | Label only | - -## Notes - -The compose stack ships sane defaults (admin creds, `ROOT_URL`, `HTTP_PORT=3000`) -via `.env.example`; override by editing the stack's `.env` before deploy. The web -UI listens on port 3000 — open it on the box firewall (the `admin-rocketchat` box -template does). diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml deleted file mode 100644 index dafc00b..0000000 --- a/02_ansible_layer/admin/roles/software.install.rocketchat/defaults/main.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -# software.install.rocketchat — deploy the Rocket.Chat docker-compose stack. -# -# The stack itself lives in the catalog at -# 03_container_layer/docker/admin/rocketchat/ -# (Rocket.Chat + MongoDB replica set + provisioner). This role rsyncs that -# directory onto the target box and brings it up via docker compose, reusing the -# shared software.configure.docker-compose role. - -# Controller-side source: resolved from the workspace RANGE42_INVENTORY env var -# (exported by range42-context). Mirrors how CTF stacks resolve -# RANGE42_INVENTORY__DOCKER__CTF at deploy time. -ROCKETCHAT_LOCAL_PROJECT_DIR: "{{ lookup('env', 'RANGE42_INVENTORY') }}/03_container_layer/docker/admin/rocketchat" - -# Where the stack is staged + run on the target box. -ROCKETCHAT_REMOTE_PROJECT_DIR: "/opt/range42/rocketchat" - -# Owner of the staged files on the box: the scenario cloud-init admin user. -# Matches the canonical OPERATOR_USER wiring (no fallback — fail loudly if the -# scenario never set it, rather than silently chown to a user that may not exist). -ROCKETCHAT_OPERATOR_USER: "{{ default_admin_vm_ci_user }}" - -# Container name docker-compose polls for after `up` — matches compose.yml. -ROCKETCHAT_CONTAINER_NAME: "rocketchat" - -# Label only; admin service (not a CTF target). -ROCKETCHAT_LABEL_PROJECT_TYPE: "admin" diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml deleted file mode 100644 index 62522da..0000000 --- a/02_ansible_layer/admin/roles/software.install.rocketchat/meta/main.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -dependencies: [] -galaxy_info: - role_name: software_install_rocketchat - namespace: range42 - author: range42 - description: Deploy the Rocket.Chat docker-compose stack (admin collaboration service). - license: GPL-3.0-or-later - min_ansible_version: "2.14" - platforms: - - name: Ubuntu - versions: [noble] - - name: Debian - versions: [trixie] diff --git a/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml b/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml deleted file mode 100644 index eeb2dc7..0000000 --- a/02_ansible_layer/admin/roles/software.install.rocketchat/tasks/main.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -## -## software.install.rocketchat -## -## Deploys the catalog Rocket.Chat docker-compose stack onto the target box by -## delegating to the shared software.configure.docker-compose role. Assumes the -## Docker engine + compose plugin are already present (wire -## software.install.warmup.basic_packages with INSTALL_PACKAGES_DOCKER first). -## - -- name: DEPLOY - rocket.chat docker-compose stack - ansible.builtin.include_role: - name: software.configure.docker-compose - vars: - LOCAL__PROJECT_DIR: "{{ ROCKETCHAT_LOCAL_PROJECT_DIR }}" - REMOTE_PROJECT_DIR: "{{ ROCKETCHAT_REMOTE_PROJECT_DIR }}" - OPERATOR_USER: "{{ ROCKETCHAT_OPERATOR_USER }}" - LABEL_PROJECT_TYPE: "{{ ROCKETCHAT_LABEL_PROJECT_TYPE }}" - LABEL_PROJET_NAME: "{{ ROCKETCHAT_CONTAINER_NAME }}" - SEND_POC_DIR: "NO" - CLEAN_UP_DEPLOY_DIR: "NO" - -#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml index 295054e..35a0901 100644 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/_main.yaml @@ -3,9 +3,9 @@ - name: INCLUDE - install - docker block: # - - name: install - docker - debian-based - ansible.builtin.include_tasks: ./debian-based/docker.yaml - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] + - name: install - docker - ubuntu + ansible.builtin.include_tasks: ./ubuntu/docker.yaml + when: ansible_facts.distribution == 'Ubuntu' # - name: install - docker - fedora ansible.builtin.include_tasks: ./fedora/docker.yaml @@ -18,9 +18,9 @@ - name: INCLUDE - install - docker-compose block: # - - name: install - docker-compose - debian-based - ansible.builtin.include_tasks: ./debian-based/docker_compose.yaml - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] + - name: install - docker-compose - ubuntu + ansible.builtin.include_tasks: ./ubuntu/docker_compose.yaml + when: ansible_facts.distribution == 'Ubuntu' # - name: install - docker-compose - fedora ansible.builtin.include_tasks: ./fedora/docker_compose.yaml diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml deleted file mode 100644 index 88cfdd6..0000000 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker.yaml +++ /dev/null @@ -1,80 +0,0 @@ ---- -- name: install docker - requirements - fedora - block: - # - - name: install docker - requirements for docker-compose and docker - debian-based - ansible.builtin.apt: - name: - - apt-transport-https - - ca-certificates - - curl - # software-properties-common: Ubuntu-only here — absent from Debian - # trixie 'main', and unused anyway (the Docker repo is added via the - # apt_repository/apt_key modules, not add-apt-repository). - - make - - gcc - - python3-pip - - python3-dev - - libffi-dev - - libssl-dev - state: present - - #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - - # apt-key is removed on Debian 12+/trixie (and deprecated on Ubuntu), so the - # apt_key module fails ("apt-key not found"). Use the modern keyring method: - # fetch the armored key into /etc/apt/keyrings and pin it via signed-by=. - - name: install docker - ensure /etc/apt/keyrings exists - ansible.builtin.file: - path: /etc/apt/keyrings - state: directory - mode: "0755" - - # - - name: install docker - add docker official GPG key (keyring) - ansible.builtin.get_url: - url: https://download.docker.com/linux/{{ ansible_facts.distribution | lower }}/gpg - dest: /etc/apt/keyrings/docker.asc - mode: "0644" - - # - - name: install docker - add Docker CE repository - ansible.builtin.apt_repository: - repo: "deb [arch={{ 'arm64' if ansible_facts.architecture == 'aarch64' else 'amd64' }} signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/{{ ansible_facts.distribution | lower }} {{ ansible_distribution_release }} stable" - filename: docker - state: present - - # - - name: install docker - docker-ce - debian-based - ansible.builtin.apt: - name: - - docker-ce - - docker-ce-cli - - containerd.io - update_cache: yes - state: present - - # - - name: install docker - enable docker service - ansible.builtin.systemd: - name: docker - enabled: yes - state: started - - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] - -#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - -- name: check docker - checks debug - debian-based - block: - - name: check - docker installation - ansible.builtin.command: - cmd: docker version - register: result - - - name: check - docker version - ansible.builtin.debug: - msg: "{{ result.stdout }}" - when: result is defined - - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml deleted file mode 100644 index f86c602..0000000 --- a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/debian-based/docker_compose.yaml +++ /dev/null @@ -1,58 +0,0 @@ ---- -# -- name: install docker-compose - requirements - debian-based - block: - # - - name: install docker-compose - requirements - debian-based - ansible.builtin.apt: - name: - - apt-transport-https - - ca-certificates - - curl - # software-properties-common: Ubuntu-only — absent from Debian trixie - # 'main' and unused here (repo added via apt_repository + signed-by). - - make - - gcc - - python3-pip - - python3-dev - - libffi-dev - - libssl-dev - # python Docker SDK — required by the community.docker modules - # (docker_compose_v2 / docker_container_info) used to deploy stacks. - - python3-docker - state: present - - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] - -# -# Docker Compose v2 is the `docker compose` CLI plugin (not the old standalone -# `docker-compose` binary). community.docker.docker_compose_v2 shells out to -# `docker compose`, so install the official plugin from the docker-ce repo -# (added by docker.yaml). The previous standalone-binary download used a v1-era -# asset name (docker-compose-Linux-x86_64) that 404s for v2 releases. -- name: install docker-compose - compose v2 plugin - debian-based - ansible.builtin.apt: - name: - - docker-compose-plugin - update_cache: true - state: present - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] - -#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### - -- name: check docker-compose - checks debug - debian-based - block: - # - - name: check - docker compose v2 installation - ansible.builtin.command: - cmd: docker compose version - register: result - changed_when: false - - # - - name: check - docker compose version - ansible.builtin.debug: - msg: "{{ result.stdout }}" - when: result is defined - - when: ansible_facts.distribution in ['Ubuntu', 'Debian'] diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml new file mode 100644 index 0000000..6f1f200 --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker.yaml @@ -0,0 +1,66 @@ +--- +- name: install docker - requirements - fedora + block: + # + - name: install docker - requirements for docker-compose and docker - ubuntu + ansible.builtin.apt: + name: + - apt-transport-https + - ca-certificates + - curl + - software-properties-common + - make + - gcc + - python3-pip + - python3-dev + - libffi-dev + - libssl-dev + state: present + + #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + + - name: install docker - add docker official GPG key + ansible.builtin.apt_key: + url: https://download.docker.com/linux/ubuntu/gpg + state: present + + # + - name: install docker - add Docker CE repository + ansible.builtin.apt_repository: + repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable + state: present + + # + - name: install docker - docker-ce - ubuntu + ansible.builtin.apt: + name: + - docker-ce + - docker-ce-cli + - containerd.io + update_cache: yes + state: present + + # + - name: install docker - enable docker service + ansible.builtin.systemd: + name: docker + enabled: yes + state: started + + when: ansible_facts.distribution == 'Ubuntu' + +#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + +- name: check docker - checks debug - ubuntu + block: + - name: check - docker installation + ansible.builtin.command: + cmd: docker version + register: result + + - name: check - docker version + ansible.builtin.debug: + msg: "{{ result.stdout }}" + when: result is defined + + when: ansible_facts.distribution == 'Ubuntu' diff --git a/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml new file mode 100644 index 0000000..a3408ec --- /dev/null +++ b/02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/docker/ubuntu/docker_compose.yaml @@ -0,0 +1,57 @@ +--- +# +- name: install docker - requirements - fedora + block: + # + - name: install docker - requirements for docker-compose and docker - ubuntu + ansible.builtin.apt: + name: + - apt-transport-https + - ca-certificates + - curl + - software-properties-common + - make + - gcc + - python3-pip + - python3-dev + - libffi-dev + - libssl-dev + state: present + + when: ansible_facts.distribution == 'Ubuntu' + +# +- name: install docker-compose - get_url docker-compose + block: + # + - name: DOWNLOAD - docker-compose + ansible.builtin.get_url: + url: "https://github.com/docker/compose/releases/download/v{{ DOCKER_COMPOSE_VERSION | default('2.39.1') }}/docker-compose-Linux-x86_64" + dest: "/usr/local/bin/docker-compose" + mode: "0755" + + # + - name: install docker-compose - chmod +x docker-compose + ansible.builtin.file: + path: /usr/local/bin/docker-compose + mode: "u+x,g+x" + + when: ansible_facts.distribution == 'Ubuntu' + +#### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### + +- name: check docker-compose - checks debug - ubuntu + block: + # + - name: check - docker-compose installation + ansible.builtin.command: + cmd: docker-compose --version + register: result + + # + - name: check - docker-compose version + ansible.builtin.debug: + msg: "{{ result.stdout }}" + when: result is defined + + when: ansible_facts.distribution == 'Ubuntu' diff --git a/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml b/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml deleted file mode 100644 index 4fbacf8..0000000 --- a/05_topology_layer/box_templates/admin-rocketchat/v1.0.0/template.yml +++ /dev/null @@ -1,30 +0,0 @@ -id: admin-rocketchat -api_version: 1 -description: >- - Rocket.Chat admin box on Debian — provisions the Docker baseline, opens the - service port, and bootstraps the Rocket.Chat compose stack (Rocket.Chat + - MongoDB replica set + provisioner) from - 03_container_layer/docker/admin/rocketchat via software.install.rocketchat. -template_vm: "template-vm-debian-trixie-medium" -default_attachments: - # host firewall — 22 (ssh) + 3000 (Rocket.Chat HTTP, HTTP_PORT default) - - kind: role - catalog_ref: software.configure.firewalls - params: - firewall_rules: - - {ip: "all", port: 22, protocol: "tcp"} # ssh - - {ip: "all", port: 3000, protocol: "tcp"} # rocketchat web ui - # Docker baseline: engine + compose plugin + operator utilities. - # NTP sync is intentionally NOT enabled: in an air-gapped / egress-filtered - # range, udp/123 is blocked, systemd-timesyncd never reaches a server, and the - # role's hard "wait for NTPSynchronized" fails. VMs take host (kvm-clock) time. - - kind: role - catalog_ref: software.install.warmup.basic_packages - params: - INSTALL_PACKAGES_BASICS: "YES" - INSTALL_PACKAGES_DOCKER: "YES" - INSTALL_PACKAGES_DOCKER_COMPOSE: "YES" - INSTALL_PACKAGES_UTILS_JSON: "YES" - INSTALL_PACKAGES_UTILS_NETWORK: "YES" - # Bootstrap Rocket.Chat: rsync the catalog compose stack + docker compose up - - {kind: role, catalog_ref: software.install.rocketchat, params: {}} From 807131f035bfd0d14813d29e843430a39e1ee562 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Fri, 12 Jun 2026 10:41:22 +0200 Subject: [PATCH 5/5] fix(rocketchat): rename default admin username to admin --- .../docker/admin/rocketchat/.env.example | 2 +- .../docker/admin/rocketchat/README.md | 14 +++++++------- .../docker/admin/rocketchat/compose.yml | 2 +- .../rocketchat/provisioning/provision-users.sh | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/03_container_layer/docker/admin/rocketchat/.env.example b/03_container_layer/docker/admin/rocketchat/.env.example index 78c9e37..8d9705c 100644 --- a/03_container_layer/docker/admin/rocketchat/.env.example +++ b/03_container_layer/docker/admin/rocketchat/.env.example @@ -8,7 +8,7 @@ RC_BASE_URL=http://localhost:3000 # ── Admin credentials ───────────────────────────────────────────────────────── # The primary admin is created automatically by the official Rocket.Chat image # via ADMIN_USERNAME / ADMIN_PASS at first boot. -RC_ADMIN_USER=rc-admin +RC_ADMIN_USER=admin RC_ADMIN_PASS=Admin1234! RC_ADMIN_EMAIL=admin@range42.local diff --git a/03_container_layer/docker/admin/rocketchat/README.md b/03_container_layer/docker/admin/rocketchat/README.md index a678678..870c909 100644 --- a/03_container_layer/docker/admin/rocketchat/README.md +++ b/03_container_layer/docker/admin/rocketchat/README.md @@ -32,7 +32,7 @@ make keys # full credentials JSON > **MongoDB replica set** initialises automatically via the `mongo-init-replica` one-shot container. No manual `rs.initiate()` step is needed. The web UI is available at `http://localhost:3000` (or `RC_BASE_URL`). -Default admin credentials: `rc-admin` / `Admin1234!` (change in `.env`). +Default admin credentials: `admin` / `Admin1234!` (change in `.env`). --- @@ -54,7 +54,7 @@ Given the default `.env.example` values (`RC_TEAMS=team-blue,team-red`, `RC_INST | Username | Role | Team | |---|---|---| -| `rc-admin` | admin | admin | +| `admin` | admin | admin | | `instructors` | admin | instructors | | `team-blue-lead` | admin | team-blue | | `team-blue-user1` | user | team-blue | @@ -81,7 +81,7 @@ make keys `tokens.txt` format: ``` -rc-admin:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +admin:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx team-blue-lead:yyyyyyyyyyyyyyyyyyyyyyyyyyyy ``` @@ -91,7 +91,7 @@ team-blue-lead:yyyyyyyyyyyyyyyyyyyyyyyyyyyy "service": "rocketchat", "baseurl": "http://localhost:3000", "users": [ - {"username": "rc-admin", "role": "admin", "team": "admin", "password": "Admin1234!"}, + {"username": "admin", "role": "admin", "team": "admin", "password": "Admin1234!"}, {"username": "team-blue-lead", "role": "admin", "team": "team-blue", "password": "R42!..."}, {"username": "team-blue-user1","role": "user", "team": "team-blue", "password": "R42!..."} ] @@ -122,7 +122,7 @@ docker push registry.example.com/range42/rocketchat-provisioner:latest | `RC_HTTP_PORT` | `3000` | Host port mapped to Rocket.Chat | | `RC_HOSTNAME` | `localhost` | Hostname for URL construction | | `RC_BASE_URL` | `http://localhost:3000` | Public URL (`ROOT_URL` in Rocket.Chat) | -| `RC_ADMIN_USER` | `rc-admin` | Bootstrap admin username | +| `RC_ADMIN_USER` | `admin` | Bootstrap admin username | | `RC_ADMIN_PASS` | `Admin1234!` | Bootstrap admin password | | `RC_ADMIN_EMAIL` | `admin@range42.local` | Bootstrap admin email | | `RC_TEAMS` | `team-blue,team-red` | Comma-separated team names | @@ -142,7 +142,7 @@ curl http://localhost:3000/api/v1/info # Authenticate and store credentials LOGIN=$(curl -sf -X POST http://localhost:3000/api/v1/login \ -H "Content-Type: application/json" \ - -d '{"username":"rc-admin","password":"Admin1234!"}') + -d '{"username":"admin","password":"Admin1234!"}') USER_ID=$(echo "${LOGIN}" | jq -r '.data.userId') AUTH_TOKEN=$(echo "${LOGIN}" | jq -r '.data.authToken') @@ -178,7 +178,7 @@ docker exec rocketchat-mongodb mongosh --eval \ ### Provisioner exits with auth error -**Symptom:** `[provision-users] ERROR: Failed to authenticate as rc-admin.` +**Symptom:** `[provision-users] ERROR: Failed to authenticate as admin.` **Cause:** Rocket.Chat is not yet fully ready (can take 60–90 s on first boot). The provisioner waits up to 180 s then exits with an error. diff --git a/03_container_layer/docker/admin/rocketchat/compose.yml b/03_container_layer/docker/admin/rocketchat/compose.yml index d751419..7e12b0b 100644 --- a/03_container_layer/docker/admin/rocketchat/compose.yml +++ b/03_container_layer/docker/admin/rocketchat/compose.yml @@ -45,7 +45,7 @@ services: ROOT_URL: ${RC_BASE_URL:-http://localhost:3000} PORT: "3000" DEPLOY_PLATFORM: docker - ADMIN_USERNAME: ${RC_ADMIN_USER:-rc-admin} + ADMIN_USERNAME: ${RC_ADMIN_USER:-admin} ADMIN_PASS: ${RC_ADMIN_PASS:-Admin1234!} ADMIN_EMAIL: ${RC_ADMIN_EMAIL:-admin@range42.local} OVERWRITE_SETTING_Show_Setup_Wizard: completed diff --git a/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh b/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh index 8f3841f..3c8b468 100755 --- a/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh +++ b/03_container_layer/docker/admin/rocketchat/provisioning/provision-users.sh @@ -4,7 +4,7 @@ # # Reads env vars: # RC_URL — RC internal URL (default: http://rocketchat:3000) -# RC_ADMIN_USER — bootstrap admin name (default: rc-admin) +# RC_ADMIN_USER — bootstrap admin name (default: admin) # RC_ADMIN_PASS — bootstrap admin pass (default: Admin1234!) # RC_TEAMS — comma-separated team names (default: team-blue,team-red) # RC_INSTRUCTOR_ORG — instructor prefix (default: instructors) @@ -18,7 +18,7 @@ set -euo pipefail RC_URL="${RC_URL:-http://rocketchat:3000}" -RC_ADMIN_USER="${RC_ADMIN_USER:-rc-admin}" +RC_ADMIN_USER="${RC_ADMIN_USER:-admin}" RC_ADMIN_PASS="${RC_ADMIN_PASS:-Admin1234!}" RC_TEAMS="${RC_TEAMS:-team-blue,team-red}" RC_INSTRUCTOR_ORG="${RC_INSTRUCTOR_ORG:-instructors}"