coreos: sish edge config for production
- sish config mirrors kubernetes/base/sish (HTTP by Host on :80, SNI passthrough on :443, raw TCP on 22 and 20000-20099), single tenant via bind-any-host, domain tunnel.traberph.de - host key and connector key provisioned from gitignored coreos/.secrets/ - MemoryMax=256M for sish - Zincati reboot window 03:00-04:00 UTC, daily sish update within v2 with rollback - ignore *.ign (contains the host key) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,3 +11,6 @@ kubeconfig
|
|||||||
|
|
||||||
# Retired dev edge credentials (cirrus_dev), kept locally only
|
# Retired dev edge credentials (cirrus_dev), kept locally only
|
||||||
old/
|
old/
|
||||||
|
|
||||||
|
# Ignition output embeds the secrets above
|
||||||
|
*.ign
|
||||||
|
|||||||
+147
-22
@@ -1,29 +1,37 @@
|
|||||||
variant: fcos
|
variant: fcos
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
# cirrus: 5001 admin sshd | 5002 sish SSH | 22,80,443 raw TCP -> k8s gateway
|
# cirrus: 5001 admin sshd | 5002 sish SSH | 80 HTTP by Host | 443 TLS by SNI (passthrough)
|
||||||
|
# 22, 20000-20099 raw TCP forwards
|
||||||
|
|
||||||
passwd:
|
passwd:
|
||||||
users:
|
users:
|
||||||
- name: core
|
- name: core
|
||||||
ssh_authorized_keys:
|
ssh_authorized_keys:
|
||||||
- ssh-ed25519 AAAA_REPLACE_ADMIN_KEY admin
|
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILL4RXmGVhbcCdh3a6TdgR+7EER250UUDk0VtrwUvb9E philipp@philipp-laptop
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
directories:
|
directories:
|
||||||
- path: /var/lib/sish/keys # sish host key, generated on first start
|
- path: /var/lib/sish/keys
|
||||||
mode: 0700
|
mode: 0700
|
||||||
user: { id: 65532 }
|
user: { id: 65532 }
|
||||||
group: { id: 65532 }
|
group: { id: 65532 }
|
||||||
files:
|
files:
|
||||||
|
# Edge SSH host key (connectors pin its public half). Kept only locally in coreos/.secrets/
|
||||||
|
# (gitignored), so back it up outside this folder. Build: butane --files-dir coreos ...
|
||||||
|
- path: /var/lib/sish/keys/ssh_host_ed25519_key
|
||||||
|
mode: 0400
|
||||||
|
user: { id: 65532 }
|
||||||
|
group: { id: 65532 }
|
||||||
|
contents:
|
||||||
|
local: .secrets/ssh_host_ed25519_key
|
||||||
- path: /var/lib/sish/pubkeys/clients # k8s tunnel client keys (authorized_keys format)
|
- path: /var/lib/sish/pubkeys/clients # k8s tunnel client keys (authorized_keys format)
|
||||||
mode: 0644
|
mode: 0644
|
||||||
contents:
|
contents:
|
||||||
inline: |
|
inline: |
|
||||||
ssh-ed25519 AAAA_REPLACE_CLIENT_KEY k8s-tunnel
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEsHP4H4HLHCEhycaAV+YZZV/HOr7HSiDkgpMA+WLO56 connector-cumulus
|
||||||
- path: /etc/ssh/sshd_config.d/10-cirrus.conf
|
- path: /etc/ssh/sshd_config.d/10-cirrus.conf
|
||||||
mode: 0644
|
mode: 0644
|
||||||
contents:
|
contents:
|
||||||
|
|
||||||
inline: |
|
inline: |
|
||||||
Port 5001
|
Port 5001
|
||||||
AuthenticationMethods publickey
|
AuthenticationMethods publickey
|
||||||
@@ -34,6 +42,65 @@ storage:
|
|||||||
contents:
|
contents:
|
||||||
inline: |
|
inline: |
|
||||||
(allow sshd_t commplex_link_port_t (tcp_socket (name_bind)))
|
(allow sshd_t commplex_link_port_t (tcp_socket (name_bind)))
|
||||||
|
# Same sish config as kubernetes/base/sish/config.yml (+ the cirrus overlay values),
|
||||||
|
# only the SSH port differs (5002 instead of 2222).
|
||||||
|
- path: /etc/sish/config.yml
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
# Listeners
|
||||||
|
ssh-address: ":5002"
|
||||||
|
http-address: ":80"
|
||||||
|
https: false # sish never terminates TLS; :443 is an SNI passthrough listener
|
||||||
|
|
||||||
|
# Single tenant: connectors may claim any hostname containing a dot, wildcards included
|
||||||
|
# (*.example.com). Only a name without a dot falls back to <name>.<domain>.
|
||||||
|
bind-any-host: true
|
||||||
|
verify-dns: false # _sish TXT ownership checks, pointless with bind-any-host
|
||||||
|
domain: tunnel.traberph.de # the edge's own name (A/AAAA -> VPS)
|
||||||
|
|
||||||
|
# SNI passthrough + multiple connectors per hostname
|
||||||
|
sni-proxy: true
|
||||||
|
sni-load-balancer: true
|
||||||
|
tcp-load-balancer: true
|
||||||
|
http-load-balancer: true
|
||||||
|
|
||||||
|
# Connectors get exactly what they ask for, or the bind fails
|
||||||
|
bind-random-ports: false
|
||||||
|
bind-random-subdomains: false
|
||||||
|
bind-random-aliases: false
|
||||||
|
force-requested-subdomains: true
|
||||||
|
force-requested-ports: true
|
||||||
|
bind-wildcards: true
|
||||||
|
# Ports connectors may claim. Must match the nftables rule below, otherwise a claimed
|
||||||
|
# port is silently unreachable.
|
||||||
|
# 22 gitea ssh, 443 SNI, 20000-20099 reserved for raw tcp forwards.
|
||||||
|
port-bind-range: "22,443,20000-20099"
|
||||||
|
|
||||||
|
# PROXY header version for connectors that request it (sish-client default: v2)
|
||||||
|
proxy-protocol: true
|
||||||
|
proxy-protocol-version: "2"
|
||||||
|
|
||||||
|
# Default is 5s, which kills idle websockets/SSE/slow uploads
|
||||||
|
idle-connection-timeout: 1h
|
||||||
|
|
||||||
|
# Auth: public keys only
|
||||||
|
authentication: true
|
||||||
|
authentication-keys-directory: /pubkeys
|
||||||
|
private-keys-directory: /keys
|
||||||
|
|
||||||
|
# No web UI / consoles
|
||||||
|
redirect-root: false
|
||||||
|
admin-console: false
|
||||||
|
service-console: false
|
||||||
|
load-templates: false
|
||||||
|
# Default -1 makes the HTTP muxer io.ReadAll() every request/response body into memory
|
||||||
|
# (for the console), even with consoles disabled: large uploads OOM-kill sish.
|
||||||
|
# 0 = never buffer, stream bodies through.
|
||||||
|
service-console-max-content-length: 0
|
||||||
|
|
||||||
|
log-to-stdout: true
|
||||||
|
log-to-file: false
|
||||||
- path: /etc/containers/systemd/sish.container
|
- path: /etc/containers/systemd/sish.container
|
||||||
mode: 0644
|
mode: 0644
|
||||||
contents:
|
contents:
|
||||||
@@ -51,30 +118,65 @@ storage:
|
|||||||
AddCapability=CAP_NET_BIND_SERVICE
|
AddCapability=CAP_NET_BIND_SERVICE
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
ReadOnly=true
|
ReadOnly=true
|
||||||
Volume=/var/lib/sish/keys:/keys:Z
|
Volume=/etc/sish:/config:ro,Z
|
||||||
|
Volume=/var/lib/sish/keys:/keys:ro,Z
|
||||||
Volume=/var/lib/sish/pubkeys:/pubkeys:ro,Z
|
Volume=/var/lib/sish/pubkeys:/pubkeys:ro,Z
|
||||||
Exec=--ssh-address=:5002 \
|
Exec=--config=/config/config.yml
|
||||||
--domain=cirrus.example.com \
|
|
||||||
--private-keys-directory=/keys \
|
|
||||||
--authentication-keys-directory=/pubkeys \
|
|
||||||
--http-address=127.0.0.1:5080 \
|
|
||||||
--http-request-port-override=5080 \
|
|
||||||
--https-request-port-override=5080 \
|
|
||||||
--port-bind-range=22,80,443 \
|
|
||||||
--bind-random-ports=false \
|
|
||||||
--force-requested-ports=true \
|
|
||||||
--force-tcp-address=true \
|
|
||||||
--tcp-load-balancer=true \
|
|
||||||
--idle-connection=false \
|
|
||||||
--proxy-protocol=true \
|
|
||||||
--proxy-protocol-version=userdefined
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
|
MemoryMax=256M
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
# OS updates: Zincati stages new FCOS releases automatically, this limits the reboot to a window
|
||||||
|
- path: /etc/zincati/config.d/55-updates-strategy.toml
|
||||||
|
mode: 0644
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
[updates]
|
||||||
|
strategy = "periodic"
|
||||||
|
[[updates.periodic.window]]
|
||||||
|
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||||
|
start_time = "03:00" # UTC
|
||||||
|
length_minutes = 60
|
||||||
|
# sish updates: no floating tags upstream (only vX.Y.Z), so podman auto-update can't follow a
|
||||||
|
# version line. This bumps Image= to the newest tag within TRACK and rolls back if sish
|
||||||
|
# doesn't come up again.
|
||||||
|
- path: /usr/local/bin/sish-update
|
||||||
|
mode: 0755
|
||||||
|
contents:
|
||||||
|
inline: |
|
||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
TRACK=v2. # "v2." = minor + patch releases, "v2.24." = patch releases only
|
||||||
|
unit=/etc/containers/systemd/sish.container
|
||||||
|
repo=ghcr.io/antoniomika/sish
|
||||||
|
|
||||||
|
current=$(sed -n "s|^Image=$repo:||p" "$unit")
|
||||||
|
latest=$(podman search --list-tags --limit 10000 --format '{{.Tag}}' "$repo" \
|
||||||
|
| grep -E "^${TRACK//./\\.}[0-9]+(\.[0-9]+)*$" | sort -V | tail -n1)
|
||||||
|
if [[ -z $latest || $(printf '%s\n' "$current" "$latest" | sort -V | tail -n1) == "$current" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_image() {
|
||||||
|
sed -i "s|^Image=.*|Image=$repo:$1|" "$unit"
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart sish.service
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "sish: $current -> $latest"
|
||||||
|
podman pull -q "$repo:$latest" >/dev/null
|
||||||
|
set_image "$latest"
|
||||||
|
sleep 30
|
||||||
|
if ! ss -Htln 'sport = :5002' | grep -q .; then
|
||||||
|
echo "sish $latest not listening on :5002, rolling back to $current" >&2
|
||||||
|
set_image "$current"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
podman image prune -af >/dev/null
|
||||||
- path: /etc/sysconfig/nftables.conf
|
- path: /etc/sysconfig/nftables.conf
|
||||||
mode: 0600
|
mode: 0600
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@@ -91,7 +193,7 @@ storage:
|
|||||||
meta l4proto { icmp, ipv6-icmp } accept
|
meta l4proto { icmp, ipv6-icmp } accept
|
||||||
udp sport 67 udp dport 68 accept
|
udp sport 67 udp dport 68 accept
|
||||||
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept
|
ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept
|
||||||
tcp dport { 22, 80, 443, 5001, 5002 } accept
|
tcp dport { 22, 80, 443, 5001, 5002, 20000-20099 } accept # sish ports: see port-bind-range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,4 +216,27 @@ systemd:
|
|||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
- name: nftables.service
|
- name: nftables.service
|
||||||
enabled: true
|
enabled: true
|
||||||
|
- name: sish-update.service
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Update sish within its major version
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target sish.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/local/bin/sish-update
|
||||||
|
- name: sish-update.timer
|
||||||
|
enabled: true
|
||||||
|
contents: |
|
||||||
|
[Unit]
|
||||||
|
Description=Daily sish update check
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-* 05:00:00 UTC
|
||||||
|
RandomizedDelaySec=30m
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user