Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6e0e44b6a | ||
|
|
39b7fc7fc3 | ||
|
|
33dd968a25 | ||
|
|
d8813a68b7 | ||
|
|
9dcaf42f96 | ||
|
|
d033503fc3 | ||
|
|
66c56288a7 | ||
|
|
2b309dec92 | ||
|
|
396f839567 | ||
|
|
2e412e6448 | ||
|
|
7f61ad0ce9 | ||
|
|
6503161146 | ||
|
|
01133ca106 | ||
|
|
ce5be4d067 | ||
|
|
150e8229fd | ||
|
|
0247b3dfd5 | ||
|
|
9b7a64e48c | ||
|
|
aee26df4b8 | ||
|
|
0bf2cd309e | ||
|
|
73d85b4aa8 | ||
|
|
b78cacb6c4 | ||
|
|
b753ba4776 | ||
|
|
84c0561146 | ||
|
|
ae8ce26a01 |
@@ -35,25 +35,25 @@ when:
|
||||
steps:
|
||||
- name: model-selection-tests
|
||||
depends_on: []
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
commands:
|
||||
- sh scripts/ci/test-model-selection.sh
|
||||
|
||||
- name: pr-review-mistral-tests
|
||||
depends_on: []
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
commands:
|
||||
- sh scripts/ci/test-pr-review-mistral.sh
|
||||
|
||||
- name: promtool-rules-guard-tests
|
||||
depends_on: []
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
commands:
|
||||
- sh scripts/ci/test-promtool-rules.sh
|
||||
|
||||
- name: yaml-citations-guard-tests
|
||||
depends_on: []
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
commands:
|
||||
- sh scripts/ci/test-yaml-citations.sh
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
when:
|
||||
# Deploy only on main (merge) + manual — never auto-deploy from a branch.
|
||||
- event: push
|
||||
branch: main
|
||||
path:
|
||||
- "ansible/roles/ssh-ca/**"
|
||||
- "ansible/plays/configure-ssh-ca.yml"
|
||||
- "ansible/inventory/pve_hosts.yml"
|
||||
- ".woodpecker/deploy-ssh-ca.yml"
|
||||
- event: deployment
|
||||
evaluate: 'CI_PIPELINE_DEPLOY_TARGET == "deploy-ssh-ca"'
|
||||
|
||||
timeout: 30
|
||||
|
||||
steps:
|
||||
- name: deploy-ssh-ca
|
||||
image: gitea.renner.uno/gitea_admin/ci-ansible:2026-04-29
|
||||
environment:
|
||||
ANSIBLE_FORCE_COLOR: "true"
|
||||
ANSIBLE_HOST_KEY_CHECKING: "false"
|
||||
ANSIBLE_ROLES_PATH: "../roles:./roles"
|
||||
SSH_PRIVATE_KEY:
|
||||
from_secret: ssh_private_key
|
||||
VAULT_PASS:
|
||||
from_secret: vault_pass
|
||||
commands:
|
||||
- mkdir -p ~/.ssh
|
||||
- echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519
|
||||
- chmod 600 ~/.ssh/id_ed25519
|
||||
- printf "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n" > ~/.ssh/config
|
||||
- cd ansible
|
||||
- echo "$VAULT_PASS" > .vault_pass && chmod 600 .vault_pass
|
||||
- export ANSIBLE_VAULT_PASSWORD_FILE=.vault_pass
|
||||
- ansible-playbook -i inventory/ plays/configure-ssh-ca.yml -v
|
||||
+1
-1
@@ -26,7 +26,7 @@ steps:
|
||||
# fetch-models.sh response mapping. No cluster, no secrets, no network — the
|
||||
# ci-claude image just provides sh + jq. Keeps the flag-injection regression
|
||||
# test from rotting (it exercises the exact injection shapes).
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
commands:
|
||||
- sh scripts/ci/test-model-selection.sh
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ steps:
|
||||
# this step is skipped — otherwise we'd fork-bomb PRs.
|
||||
when:
|
||||
- branch: main
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -30,7 +30,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: update-audit-agent
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.310
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.311
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -69,3 +69,9 @@ edge_host
|
||||
# home, so it must NOT be used as ansible_host.
|
||||
[claude_workstation]
|
||||
claude-ws ansible_host=10.3.40.78 ansible_user=root
|
||||
|
||||
# Hosts that trust the OpenBao SSH user CA (plays/configure-ssh-ca.yml).
|
||||
# Rolled out one host at a time: add the next host after the previous one
|
||||
# was verified.
|
||||
[ssh_ca]
|
||||
proxy-1.renner.uno
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
# Trust the OpenBao SSH user CA (ssh-client-signer) on the hosts in [ssh_ca]:
|
||||
# owner certificates (principal k.renner) for root/ladmin, Claude's 1h
|
||||
# certificates (principal claude-bot) for its own claude-bot account with
|
||||
# limited sudo. Existing authorized_keys stay untouched.
|
||||
#
|
||||
# serial: 1 so a host whose sshd rejects the change stops the run before the
|
||||
# next host is touched.
|
||||
- name: Configure the SSH user CA
|
||||
hosts: ssh_ca
|
||||
become: true
|
||||
gather_facts: true
|
||||
serial: 1
|
||||
roles:
|
||||
- ssh-ca
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
# SSH user CA (OpenBao ssh-client-signer). Hosts trust certificates signed by
|
||||
# it; the public key is fetched from OpenBao on the controller at run time.
|
||||
ssh_ca_public_key_url: "https://openbao.renner.uno/v1/ssh-client-signer/public_key"
|
||||
|
||||
# Owner certificates (role owner) carry this principal. It is accepted for
|
||||
# whichever admin login the distro has; users that do not exist are skipped.
|
||||
ssh_ca_owner_principal: k.renner
|
||||
ssh_ca_owner_users:
|
||||
- root
|
||||
- ladmin
|
||||
|
||||
# Claude's own account. Certificates (role claude, 1h) carry the principal.
|
||||
ssh_ca_claude_user: claude-bot
|
||||
ssh_ca_claude_principal: claude-bot
|
||||
|
||||
# Units claude-bot may restart/reload via sudo on this host, e.g. [netbird].
|
||||
ssh_ca_claude_sudo_units: []
|
||||
@@ -0,0 +1,2 @@
|
||||
# Public keys whose certificates sshd must reject (one per line, OpenSSH format).
|
||||
# Revoking a key also revokes every certificate issued for it.
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
- name: Fetch the SSH CA public key from OpenBao
|
||||
ansible.builtin.uri:
|
||||
url: "{{ ssh_ca_public_key_url }}"
|
||||
return_content: true
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
run_once: true
|
||||
register: ssh_ca_key
|
||||
|
||||
- name: Check the fetched CA key is an SSH public key
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ssh_ca_key.content is match('^ssh-ed25519 AAAA')
|
||||
fail_msg: "Unexpected CA key from {{ ssh_ca_public_key_url }}"
|
||||
run_once: true
|
||||
|
||||
- name: Install the trusted user CA key
|
||||
ansible.builtin.copy:
|
||||
content: "{{ ssh_ca_key.content | trim }}\n"
|
||||
dest: /etc/ssh/trusted_user_ca_keys
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
# sshd refuses all public key logins when RevokedKeys is set but unreadable,
|
||||
# so the file is always deployed, even when empty.
|
||||
- name: Install the revoked keys list
|
||||
ansible.builtin.copy:
|
||||
src: revoked_keys
|
||||
dest: /etc/ssh/revoked_keys
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Create the Claude account
|
||||
ansible.builtin.user:
|
||||
name: "{{ ssh_ca_claude_user }}"
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
password: "!"
|
||||
|
||||
- name: Look up the owner admin logins
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ item }}"
|
||||
fail_key: false
|
||||
loop: "{{ ssh_ca_owner_users }}"
|
||||
register: ssh_ca_owner_lookup
|
||||
|
||||
- name: Create the principals directory
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/auth_principals
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0755"
|
||||
|
||||
- name: Accept the owner principal for the existing admin logins
|
||||
ansible.builtin.copy:
|
||||
content: "{{ ssh_ca_owner_principal }}\n"
|
||||
dest: "/etc/ssh/auth_principals/{{ item.item }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
loop: "{{ ssh_ca_owner_lookup.results }}"
|
||||
loop_control:
|
||||
label: "{{ item.item }}"
|
||||
when: (item.ansible_facts.getent_passwd | default({})).get(item.item) is not none
|
||||
|
||||
- name: Accept the Claude principal for the Claude account
|
||||
ansible.builtin.copy:
|
||||
content: "{{ ssh_ca_claude_principal }}\n"
|
||||
dest: "/etc/ssh/auth_principals/{{ ssh_ca_claude_user }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Install the Claude sudo rules
|
||||
ansible.builtin.template:
|
||||
src: sudoers-claude.j2
|
||||
dest: "/etc/sudoers.d/{{ ssh_ca_claude_user }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0440"
|
||||
validate: visudo -cf %s
|
||||
|
||||
# A broken drop-in only bites on the next sshd restart, so it is tested here
|
||||
# and removed again if the full config does not parse.
|
||||
- name: Configure sshd for the user CA
|
||||
block:
|
||||
- name: Install the sshd drop-in
|
||||
ansible.builtin.template:
|
||||
src: ssh-ca.conf.j2
|
||||
dest: /etc/ssh/sshd_config.d/50-ssh-ca.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
|
||||
- name: Test the full sshd config
|
||||
ansible.builtin.command: sshd -t
|
||||
changed_when: false
|
||||
rescue:
|
||||
- name: Remove the drop-in that broke sshd -t
|
||||
ansible.builtin.file:
|
||||
path: /etc/ssh/sshd_config.d/50-ssh-ca.conf
|
||||
state: absent
|
||||
|
||||
- name: Fail the host
|
||||
ansible.builtin.fail:
|
||||
msg: "sshd -t failed with the SSH CA drop-in; it was removed again"
|
||||
|
||||
- name: Read the effective sshd settings
|
||||
ansible.builtin.command: sshd -T
|
||||
changed_when: false
|
||||
register: ssh_ca_effective
|
||||
|
||||
# Keyword case in sshd -T output differs between OpenSSH versions (mixed case
|
||||
# on 10.6), so the lines are compared lowercased.
|
||||
- name: Check sshd uses the CA, principals and revocation list
|
||||
vars:
|
||||
ssh_ca_effective_lines: "{{ ssh_ca_effective.stdout_lines | map('lower') | list }}"
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'trustedusercakeys /etc/ssh/trusted_user_ca_keys' in ssh_ca_effective_lines"
|
||||
- "'authorizedprincipalsfile /etc/ssh/auth_principals/%u' in ssh_ca_effective_lines"
|
||||
- "'revokedkeys /etc/ssh/revoked_keys' in ssh_ca_effective_lines"
|
||||
fail_msg: "sshd -T does not show the SSH CA settings; another config line takes precedence"
|
||||
|
||||
# The unit is sshd.service on Arch and RedHat, ssh.service on Debian. sshd -T
|
||||
# only parses the files, so the running daemon is reloaded on every run; a
|
||||
# reload keeps open sessions.
|
||||
- name: Read the service list
|
||||
ansible.builtin.service_facts:
|
||||
|
||||
- name: Reload sshd so the running daemon uses the CA config
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ 'sshd.service' if 'sshd.service' in ansible_facts.services else 'ssh.service' }}"
|
||||
state: reloaded
|
||||
@@ -0,0 +1,4 @@
|
||||
# {{ ansible_managed }}
|
||||
TrustedUserCAKeys /etc/ssh/trusted_user_ca_keys
|
||||
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
|
||||
RevokedKeys /etc/ssh/revoked_keys
|
||||
@@ -0,0 +1,6 @@
|
||||
# {{ ansible_managed }}
|
||||
# NOEXEC: journalctl/systemctl open a pager, which must not spawn a shell.
|
||||
{{ ssh_ca_claude_user }} ALL=(root) NOPASSWD:NOEXEC: /usr/bin/journalctl, /usr/bin/journalctl *, /usr/bin/systemctl status, /usr/bin/systemctl status *, /usr/bin/systemctl list-units, /usr/bin/systemctl list-units *, /usr/bin/ss -tulpn, /usr/sbin/sshd -T
|
||||
{% for unit in ssh_ca_claude_sudo_units %}
|
||||
{{ ssh_ca_claude_user }} ALL=(root) NOPASSWD:NOEXEC: /usr/bin/systemctl restart {{ unit }}, /usr/bin/systemctl reload {{ unit }}
|
||||
{% endfor %}
|
||||
@@ -706,7 +706,7 @@ data:
|
||||
# return entries instead of short-circuiting. Built from
|
||||
# the samba-bridge repo at
|
||||
# gitea.renner.uno/konairius/authentik-samba-bridge .
|
||||
container_image: gitea.renner.uno/konairius/authentik-samba-bridge-ldap-outpost:2026.8.3-9dd92dd
|
||||
container_image: gitea.renner.uno/konairius/authentik-samba-bridge-ldap-outpost:2026.8.4-3c8f413
|
||||
object_naming_template: "ak-outpost-%(name)s"
|
||||
kubernetes_namespace: authentik
|
||||
kubernetes_replicas: 1
|
||||
@@ -1093,6 +1093,72 @@ data:
|
||||
negate: false
|
||||
failure_result: false
|
||||
|
||||
# OpenBao human login (auth/oidc, role owner). Confidential client:
|
||||
# OpenBao's OIDC method requires a secret. Authentik generates it; it
|
||||
# lives only in OpenBao's auth/oidc/config, never in git.
|
||||
- id: openbao-admins-group
|
||||
model: authentik_core.group
|
||||
identifiers:
|
||||
name: OpenBao Admins
|
||||
state: present
|
||||
attrs:
|
||||
is_superuser: false
|
||||
# Members may log in to OpenBao via OIDC. Add/remove members HERE.
|
||||
users:
|
||||
- !Find [authentik_core.user, [username, k.renner]]
|
||||
|
||||
- id: openbao-provider
|
||||
model: authentik_providers_oauth2.oauth2provider
|
||||
identifiers:
|
||||
name: OpenBao
|
||||
state: present
|
||||
attrs:
|
||||
authentication_flow: null
|
||||
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
|
||||
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||
client_type: confidential
|
||||
client_id: openbao
|
||||
# See the Matrix (MAS) provider: a blueprint-made provider starts
|
||||
# with no grant types.
|
||||
grant_types:
|
||||
- authorization_code
|
||||
- refresh_token
|
||||
signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]
|
||||
property_mappings:
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-openid]]
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-profile]]
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [managed, goauthentik.io/providers/oauth2/scope-email]]
|
||||
redirect_uris:
|
||||
# bao login -method=oidc (CLI listener).
|
||||
- url: http://localhost:8250/oidc/callback
|
||||
matching_mode: strict
|
||||
- url: https://openbao.renner.uno/ui/vault/auth/oidc/oidc/callback
|
||||
matching_mode: strict
|
||||
|
||||
# The slug is the issuer path: https://auth.renner.uno/application/o/openbao/
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: openbao
|
||||
state: present
|
||||
attrs:
|
||||
name: OpenBao
|
||||
slug: openbao
|
||||
provider: !KeyOf openbao-provider
|
||||
meta_launch_url: https://openbao.renner.uno
|
||||
meta_description: "OpenBao secrets"
|
||||
|
||||
# Only the OpenBao Admins group passes.
|
||||
- model: authentik_policies.policybinding
|
||||
identifiers:
|
||||
target: !Find [authentik_core.application, [slug, openbao]]
|
||||
group: !KeyOf openbao-admins-group
|
||||
order: 0
|
||||
state: present
|
||||
attrs:
|
||||
enabled: true
|
||||
negate: false
|
||||
failure_result: false
|
||||
|
||||
# OpenProject, decommissioned. Application first: Application.provider
|
||||
# is on_delete=PROTECT. Its policy binding cascades with it.
|
||||
- model: authentik_core.application
|
||||
|
||||
@@ -105,7 +105,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: controller
|
||||
image: gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-10-25-1458 # {"$imagepolicy": "flux-system:authentik-gate-sync"}
|
||||
image: gitea.renner.uno/gitea_admin/authentik-gate-sync:2026-10-27-0801 # {"$imagepolicy": "flux-system:authentik-gate-sync"}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
# Pinned to match the samba-bridge fork's app image (server.Dockerfile
|
||||
# AUTHENTIK_VERSION). Bump in lockstep with the bridge rebuild — a
|
||||
# floating range drifts the chart ahead of the pinned app image.
|
||||
version: "2026.8.3"
|
||||
version: "2026.8.4"
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: authentik
|
||||
@@ -58,7 +58,7 @@ spec:
|
||||
# 2. Pinning means the deployed binary is traceable to a
|
||||
# specific repo commit. Bump the tag whenever a new build
|
||||
# is published from gitea.renner.uno/konairius/authentik-samba-bridge .
|
||||
tag: "2026.8.3-9dd92dd"
|
||||
tag: "2026.8.4-3c8f413"
|
||||
envFrom:
|
||||
# SAMBA_BRIDGE_DOMAIN_SID / _ENABLED_GROUP / _UID_OFFSET
|
||||
# consumed by the bridge module at Django app startup. Missing
|
||||
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: controller
|
||||
image: gitea.renner.uno/gitea_admin/cloudflare-lb-sync:2026-10-25-1504 # {"$imagepolicy": "flux-system:cloudflare-lb-sync"}
|
||||
image: gitea.renner.uno/gitea_admin/cloudflare-lb-sync:2026-10-27-0807 # {"$imagepolicy": "flux-system:cloudflare-lb-sync"}
|
||||
env:
|
||||
- name: CLOUDFLARE_API_TOKEN
|
||||
valueFrom:
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: sync
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/python
|
||||
- /opt/hermes-skills/gitea-issues/scripts/sync_webhook.py
|
||||
|
||||
@@ -51,7 +51,7 @@ spec:
|
||||
# connects from the pod network. No env equivalent exists, so it is
|
||||
# set in config.yaml on every start (idempotent).
|
||||
- name: config
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/hermes
|
||||
- config
|
||||
@@ -75,7 +75,7 @@ spec:
|
||||
mountPath: /opt/data
|
||||
# WebSocket keepalive ping interval in seconds (default 20).
|
||||
- name: config-ws-ping
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/hermes
|
||||
- config
|
||||
@@ -99,7 +99,7 @@ spec:
|
||||
mountPath: /opt/data
|
||||
# Skills from git (ConfigMap hermes-skills) beside the PVC's skills/.
|
||||
- name: skills-config
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/hermes
|
||||
- config
|
||||
@@ -130,7 +130,7 @@ spec:
|
||||
# - alert-monitor: hourly; the script wakes the agent only for
|
||||
# untriaged alerts.
|
||||
- name: cron-jobs
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
# Non-fatal: a broken jobs.json or a changed cron CLI after an image
|
||||
# bump logs a warning instead of keeping Hermes from starting.
|
||||
command:
|
||||
@@ -187,7 +187,7 @@ spec:
|
||||
readOnly: true
|
||||
# Home Assistant MCP server; token resolved from HA_TOKEN at connect, writes need approval.
|
||||
- name: config-mcp-homeassistant
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/hermes
|
||||
- config
|
||||
@@ -214,7 +214,7 @@ spec:
|
||||
# skills, the Home Assistant MCP and the tea login are re-applied on
|
||||
# every start. default gets the kanban toolset to hand cards to them.
|
||||
- name: experts
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
# Non-fatal like cron-jobs: a broken expert logs a warning.
|
||||
command:
|
||||
- sh
|
||||
@@ -305,7 +305,7 @@ spec:
|
||||
# Gitea issues -> kanban (skills/gitea-issues): route script, webhook route on default,
|
||||
# completion shell hook plus its allowlist entry on every profile.
|
||||
- name: gitea-issues
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
# Non-fatal like cron-jobs: a failure logs a warning.
|
||||
command:
|
||||
- sh
|
||||
@@ -368,7 +368,7 @@ spec:
|
||||
readOnly: true
|
||||
# Multiplexed default reads platform credentials only from .env (rationale: commit message).
|
||||
- name: default-env
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
# Fatal on a writer failure: without these Matrix and the API server stay down.
|
||||
command:
|
||||
- /opt/hermes/.venv/bin/python
|
||||
@@ -428,7 +428,7 @@ spec:
|
||||
# tea login hermes-bot, written from the mounted token into an
|
||||
# emptyDir at the agent's ~/.config/tea.
|
||||
- name: tea-login
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
@@ -457,7 +457,7 @@ spec:
|
||||
readOnly: true
|
||||
containers:
|
||||
- name: hermes
|
||||
image: gitea.renner.uno/konairius/hermes:build.1792940452 # {"$imagepolicy": "flux-system:hermes"}
|
||||
image: gitea.renner.uno/konairius/hermes:build.1793088058 # {"$imagepolicy": "flux-system:hermes"}
|
||||
args: ["gateway", "run"]
|
||||
env:
|
||||
- name: HERMES_DASHBOARD
|
||||
|
||||
@@ -30,7 +30,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: controller
|
||||
image: gitea.renner.uno/gitea_admin/kuma-sync:2026-10-25-1459 # {"$imagepolicy": "flux-system:kuma-sync"}
|
||||
image: gitea.renner.uno/gitea_admin/kuma-sync:2026-10-27-0801 # {"$imagepolicy": "flux-system:kuma-sync"}
|
||||
env:
|
||||
- name: KUMA_URL
|
||||
value: https://kuma.renner.uno
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: gotenberg
|
||||
image: gotenberg/gotenberg:8.38.1 # {"$imagepolicy": "flux-system:gotenberg"}
|
||||
image: gotenberg/gotenberg:8.39.0 # {"$imagepolicy": "flux-system:gotenberg"}
|
||||
args:
|
||||
- "gotenberg"
|
||||
- "--chromium-disable-javascript=true"
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: review-bot
|
||||
image: gitea.renner.uno/gitea_admin/review-bot:2026-10-26-0647 # {"$imagepolicy": "flux-system:review-bot"}
|
||||
image: gitea.renner.uno/gitea_admin/review-bot:2026-10-27-0757 # {"$imagepolicy": "flux-system:review-bot"}
|
||||
env:
|
||||
- name: REPOS
|
||||
value: konairius/homelab,konairius/homeassistant,konairius/authentik-samba-bridge,konairius/shelly-manager
|
||||
|
||||
@@ -53,7 +53,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: wiki
|
||||
image: gitea.renner.uno/konairius/wiki:build.1793081757 # {"$imagepolicy": "flux-system:wiki"}
|
||||
image: gitea.renner.uno/konairius/wiki:build.1793086240 # {"$imagepolicy": "flux-system:wiki"}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
|
||||
@@ -102,6 +102,30 @@ spec:
|
||||
- { type: 3, family: IPv6 }
|
||||
- { type: 4, family: IPv6 }
|
||||
---
|
||||
# OpenBao server → Authentik (auth.renner.uno) for the auth/oidc method:
|
||||
# discovery, token exchange and JWKS. auth.renner.uno resolves to the Traefik
|
||||
# VIP; Cilium applies the DNAT before egress policy, so the rule matches the
|
||||
# Traefik pods, which `world` does not cover (same path as mas-authentik-egress).
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: openbao-authentik-egress
|
||||
namespace: openbao
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: openbao
|
||||
component: server
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ingress
|
||||
app.kubernetes.io/name: traefik
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8443"
|
||||
protocol: TCP
|
||||
---
|
||||
# Egress complement to home-assistant-rotator-ingress (home-assistant-ns.yaml).
|
||||
# The openbao-ns default-deny grants TCP egress only to openbao, kube-dns, the
|
||||
# apiserver/host, and `world`. The Class-B rotators that call EXTERNAL APIs
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# OpenBao policy: ssh-sign-claude
|
||||
# Sign claude-bot's SSH public key on ssh-client-signer role claude (1h certs). Attached to userpass user claude-bot. NOT applied by Flux.
|
||||
#
|
||||
# bao write ssh-client-signer/roles/claude @kubernetes/infrastructure/openbao/ssh-ca/role-claude.json
|
||||
# bao policy write ssh-sign-claude kubernetes/infrastructure/openbao/policies/ssh-sign-claude.hcl
|
||||
# bao write auth/userpass/users/claude-bot token_policies=claude-bot-write,ssh-sign-claude
|
||||
|
||||
path "ssh-client-signer/sign/claude" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# OpenBao policy: ssh-sign-owner
|
||||
# Sign the owner's SSH public keys on ssh-client-signer role owner. Bound to auth/oidc role owner. NOT applied by Flux.
|
||||
#
|
||||
# bao secrets enable -path=ssh-client-signer -max-lease-ttl=4380h ssh # 768h default would cap the 6-month certs
|
||||
# bao write ssh-client-signer/config/ca generate_signing_key=true key_type=ed25519
|
||||
# bao write ssh-client-signer/roles/owner @kubernetes/infrastructure/openbao/ssh-ca/role-owner.json
|
||||
# bao policy write ssh-sign-owner kubernetes/infrastructure/openbao/policies/ssh-sign-owner.hcl
|
||||
# bao auth enable oidc
|
||||
# bao write auth/oidc/config oidc_discovery_url=https://auth.renner.uno/application/o/openbao/ \
|
||||
# oidc_client_id=openbao oidc_client_secret=<from Authentik> default_role=owner
|
||||
# bao write auth/oidc/role/owner role_type=oidc user_claim=preferred_username \
|
||||
# groups_claim=groups oidc_scopes=profile,email bound_claims='{"groups":["OpenBao Admins"]}' \
|
||||
# allowed_redirect_uris=http://localhost:8250/oidc/callback \
|
||||
# allowed_redirect_uris=https://openbao.renner.uno/ui/vault/auth/oidc/oidc/callback \
|
||||
# token_policies=ssh-sign-owner token_ttl=1h
|
||||
|
||||
path "ssh-client-signer/sign/owner" {
|
||||
capabilities = ["update"]
|
||||
}
|
||||
@@ -71,7 +71,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: rotate
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-26 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-27 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
args: ["ha-token"]
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -133,7 +133,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: rotate
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-26 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-27 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
args: ["ha-token"]
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
@@ -195,7 +195,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: rotate
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-26 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
image: gitea.renner.uno/gitea_admin/secret-rotator:2026-10-27 # {"$imagepolicy": "flux-system:secret-rotator"}
|
||||
args: ["ha-token"]
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"key_type": "ca",
|
||||
"allow_user_certificates": true,
|
||||
"allowed_users": "claude-bot",
|
||||
"default_user": "claude-bot",
|
||||
"allowed_extensions": "permit-pty",
|
||||
"default_extensions": {
|
||||
"permit-pty": ""
|
||||
},
|
||||
"ttl": "1h",
|
||||
"max_ttl": "1h"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"key_type": "ca",
|
||||
"allow_user_certificates": true,
|
||||
"allowed_users": "k.renner",
|
||||
"default_user": "k.renner",
|
||||
"allowed_extensions": "permit-pty,permit-port-forwarding,permit-agent-forwarding,permit-user-rc",
|
||||
"default_extensions": {
|
||||
"permit-pty": "",
|
||||
"permit-port-forwarding": "",
|
||||
"permit-user-rc": ""
|
||||
},
|
||||
"ttl": "4380h",
|
||||
"max_ttl": "4380h"
|
||||
}
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: backup-verify
|
||||
image: gitea.renner.uno/gitea_admin/ci-backup-verify:2026-10-25 # {"$imagepolicy": "flux-system:backup-verify"}
|
||||
image: gitea.renner.uno/gitea_admin/ci-backup-verify:2026-10-27 # {"$imagepolicy": "flux-system:backup-verify"}
|
||||
command: ["bash", "/script/backup-verify.sh"]
|
||||
env:
|
||||
- name: HOME
|
||||
|
||||
Reference in New Issue
Block a user