Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ reports success -- the settings simply land where nobody sees them.
- `contributor`: hyperi-ci and the tools its checks drive (semgrep, alint), gitleaks, trivy, hadolint, pip-audit, ansible-lint, pre-commit, act
- `soe` / `soe-gui`: HyperI org policy: VPN clients, Claude Code, Slack, LibreOffice, RDP client, telemetry-disable, auto-updates, GNOME taskbar
- `power-profile` (off by default, and deliberately not in `soe`): sleep, idle and lid policy, selected per machine. `always-on` (the default profile) never idle-suspends on mains power and does not sleep when the lid shuts -- for a repurposed laptop doing build work, or a desktop that has to answer ssh. `vm` never sleeps or suspends at all, for an unattended RDP guest that nobody can walk over and wake. Battery behaviour stays stock under `always-on`, because a machine that will not sleep in a bag cooks itself. Profiles are data files, so adding one is adding a file -- see [roles/power-profile/README.md](ansible/roles/power-profile/README.md)
- `arcane` (off by default): [Arcane](https://getarcane.app), a web UI for the containers on the box. Enable it with `-e soe_arcane_enabled=true` and you get a daemon on `http://localhost:3552` that comes back after a reboot and keeps itself updated. Works against docker-ce on Linux and colima on macOS. Bound to loopback because it holds the Docker socket, so whatever reaches that port owns the machine. Login is `arcane` / `Arcane-Admin1!` (set `soe_arcane_admin_password` to change it -- Arcane requires at least 12 characters with an uppercase letter, a lowercase letter, a number and a symbol). Upstream forces a password change on first login; the role performs that change itself right after deploying, so you never meet the dialog. There is still a login -- auto-login is compiled out of every published image, so zero-auth is not available without building your own
- `arcane` (off by default): [Arcane](https://getarcane.app), a web UI for the containers on the box. Enable it with `-e soe_arcane_enabled=true` and you get a daemon on `http://localhost:3552` that comes back after a reboot and keeps itself updated. Works against docker-ce on Linux and colima on macOS. Bound to loopback because it holds the Docker socket, so whatever reaches that port owns the machine. Login is whatever Arcane seeds -- `arcane` / `arcane-admin` as upstream documents it. The role sets neither, and only clears the forced first-login password prompt, which it does by re-submitting that seeded password so the credentials stay unchanged. That needs the password policy relaxed to `basic` (`soe_arcane_password_policy`), because upstream's default `strong` policy rejects its own seeded password. There is still a login -- auto-login sits behind a `buildables` Go build tag that no published image is compiled with, so zero-auth is not available without building your own image
- `local-services` (off by default): a persistent local ClickHouse and Redpanda for ad-hoc work -- somewhere to poke at a query or hand-feed a topic without waiting for a suite to build. Enable with `-e soe_local_services_enabled=true`. Deployed **stopped**: `restart: no`, so a reboot leaves them down and they cost nothing until `local-services up`, which pulls latest and takes seconds. Both capped at 1GB and bound to loopback. They are spike instances -- integration and e2e suites create and tear down their own containers, because a shared daemon makes a suite non-hermetic and order-dependent

**Desktop UI** (`winlike` or `maclike` tag): GNOME extensions, a transparent taskbar (winlike) or a dock (maclike).
Expand Down
22 changes: 8 additions & 14 deletions ansible/roles/soe/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,15 @@ soe_arcane_auto_update_exclude:
# abbreviation.
soe_arcane_timezone: UTC

# Admin password, set once while the seeded credential is still known.
# The login is whatever Arcane seeds -- this role sets neither the username nor
# the password, so upstream's documented default is what you sign in with.
#
# Arcane seeds `arcane` / `arcane-admin` with a forced password change on first
# login. The role performs that change itself straight after the first deploy,
# which clears the flag, so nobody meets the dialog.
#
# Upstream's own seeded value cannot be reused here: the API enforces at least
# 12 characters with an uppercase letter, a lowercase letter, a number and a
# symbol, and rejects anything weaker with a 400. This is the seeded name in a
# form that policy accepts.
#
# A known default rather than a generated secret: this is a loopback-only UI,
# and a password the developer already knows beats one they have to go and look
# up. Override it in group_vars on any machine where that trade does not hold.
soe_arcane_admin_password: Arcane-Admin1!
# Upstream's default policy is `strong`: 12 characters with an uppercase letter,
# a number and a symbol, which its OWN seeded password fails. Retiring the
# forced first-login prompt means re-submitting that seeded password, so the
# policy has to accept it. Eight characters remains the floor whatever this
# says -- the request schema rejects anything shorter before the policy is read.
soe_arcane_password_policy: basic

# ============================================================================
# Local services -- persistent ClickHouse + Redpanda for ad-hoc work, OPT-IN.
Expand Down
30 changes: 18 additions & 12 deletions ansible/roles/soe/tasks/arcane.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,34 +176,41 @@
no_log: true
when: not ansible_check_mode

# Selected by the global-admin flag rather than by name, so a host whose
# admin has since been renamed is still found.
- name: Work out whether Arcane still wants a password change
# noqa: var-naming[no-role-prefix] -- soe_ IS the role prefix here
ansible.builtin.set_fact:
soe_arcane_admin: >-
{{ (soe_arcane_users.json.data | default([])
| selectattr('username', 'equalto', 'arcane')
| selectattr('isGlobalAdmin', 'defined')
| selectattr('isGlobalAdmin')
| list | first) | default({}) }}
when: not ansible_check_mode

# No currentPassword needed on the admin path, so this works whatever the
# password happens to be now.
- name: Set the Arcane admin password
# Must precede the change below: the policy is read at validation time, and
# the default `strong` rejects Arcane's own seeded password.
- name: Relax the Arcane password policy
ansible.builtin.uri:
url: "http://127.0.0.1:{{ soe_arcane_port }}/api/users/{{ soe_arcane_admin.id }}"
url: "http://127.0.0.1:{{ soe_arcane_port }}/api/environments/0/settings"
method: PUT
headers:
X-Api-Key: "{{ soe_arcane_admin_api_key }}"
body_format: json
body:
password: "{{ soe_arcane_admin_password }}"
authPasswordPolicy: "{{ soe_arcane_password_policy }}"
status_code: [200]
register: soe_arcane_pwset
changed_when: soe_arcane_pwset.status == 200
no_log: true
register: soe_arcane_policy
changed_when: soe_arcane_policy.status == 200
when:
- not ansible_check_mode
- soe_arcane_admin.requiresPasswordChange | default(false)

# Changing the seeded password to itself: the flag clears, and the login
# stays the one upstream documents. `arcane-admin` is upstream's seed, not a
# value this role chooses -- it is only ever submitted while the
# forced-change flag is still up, which is before anyone could have changed
# it.
- name: Retire the forced password change
ansible.builtin.uri:
url: "http://127.0.0.1:{{ soe_arcane_port }}/api/auth/password"
Expand All @@ -212,12 +219,11 @@
X-Api-Key: "{{ soe_arcane_admin_api_key }}"
body_format: json
body:
currentPassword: "{{ soe_arcane_admin_password }}"
newPassword: "{{ soe_arcane_admin_password }}"
currentPassword: arcane-admin
newPassword: arcane-admin
status_code: [200]
register: soe_arcane_pwchange
changed_when: soe_arcane_pwchange.status == 200
no_log: true
when:
- not ansible_check_mode
- soe_arcane_admin.requiresPasswordChange | default(false)
Expand Down
Loading