initial commit: cirrus edge (Talos + sish)
- talos/: generated base config (gitignored) + patches for control-plane scheduling, unprivileged ports and the ingress firewall - kubernetes/: sish base and cirrus-dev overlay, applied with kubectl - READMEs incl. production rollout plan Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# Talos: cirrus edge node
|
||||
|
||||
Single-node Talos control plane that runs only sish. Talos and Kubernetes are managed by hand
|
||||
with `talosctl`/`kubectl` (no Flux).
|
||||
|
||||
| File | |
|
||||
|---|---|
|
||||
| `controlplane.yaml` | Base config, **unmodified** output of `talosctl gen config` (gitignored, contains the cluster PKI) |
|
||||
| `worker.yaml` | Generated worker config, unused on a single node (gitignored) |
|
||||
| `talosconfig` | Admin client config for `talosctl` (gitignored) |
|
||||
| `patches/*.yaml` | Every change to the base config |
|
||||
|
||||
```sh
|
||||
export TALOSCONFIG=talos/talosconfig # run from the repo root
|
||||
N="-n 10.20.5.130 -e 10.20.5.130"
|
||||
```
|
||||
|
||||
## Base config + patches
|
||||
|
||||
The base file is never edited by hand; all changes live in `patches/`. The node's config is
|
||||
therefore always `controlplane.yaml` + `patches/*.yaml`, which keeps changes reviewable and lets
|
||||
a newly generated base (new node, new Talos defaults) get the same changes by reapplying the
|
||||
patches. `talosctl patch mc` merges a patch into the node's live config; it does not touch the
|
||||
local files.
|
||||
|
||||
| Patch | Purpose |
|
||||
|---|---|
|
||||
| `control-plane-scheduling.yaml` | Drops the control-plane `NoSchedule` taint so sish can run on the only node |
|
||||
| `unprivileged-ports.yaml` | `ip_unprivileged_port_start=22`: sish (non-root, hostNetwork) binds :22/:80/:443 |
|
||||
| `firewall.yaml` | Ingress firewall, default block (see below) |
|
||||
|
||||
Apply a single patch (dry run first; use `--mode try` for anything that can lock you out, it
|
||||
reverts automatically unless re-applied):
|
||||
|
||||
```sh
|
||||
talosctl $N patch mc --patch @talos/patches/<patch>.yaml --mode no-reboot --dry-run
|
||||
talosctl $N patch mc --patch @talos/patches/<patch>.yaml --mode no-reboot
|
||||
```
|
||||
|
||||
Check that the node matches the files (expect `No changes.`):
|
||||
|
||||
```sh
|
||||
talosctl machineconfig patch talos/controlplane.yaml \
|
||||
$(for p in talos/patches/*.yaml; do printf -- '--patch @%s ' "$p"; done) |
|
||||
talosctl $N apply-config --file /dev/stdin --dry-run
|
||||
```
|
||||
|
||||
## Firewall
|
||||
|
||||
Default action `block`. Loopback and replies to outgoing connections are always allowed.
|
||||
|
||||
| Open | Source | |
|
||||
|---|---|---|
|
||||
| 22, 80, 443, 2222, 20000-20099 tcp | anyone | sish (2222 = connector SSH, rest = forwards) |
|
||||
| 6443, 50000 tcp | anyone | Kubernetes API, Talos API (both client-cert authenticated) |
|
||||
| 53 udp/tcp | pod network `10.244.0.0/16` | CoreDNS forwards to the Talos host DNS |
|
||||
|
||||
Closed: flannel VXLAN 4789/udp, etcd 2379-2383, kubelet 10250, kube-proxy 10256, trustd 50001
|
||||
(open it to the node network only when a second node joins).
|
||||
|
||||
The sish ports must match `port-bind-range` in `kubernetes/base/sish/config.yml`. To add a raw
|
||||
TCP port outside 20000-20099, change both files together.
|
||||
|
||||
## New node
|
||||
|
||||
```sh
|
||||
talosctl gen config <cluster-name> https://<node-ip>:6443 --install-disk <disk> -o talos/
|
||||
talosctl machineconfig patch talos/controlplane.yaml \
|
||||
$(for p in talos/patches/*.yaml; do printf -- '--patch @%s ' "$p"; done) |
|
||||
talosctl apply-config --insecure -n <node-ip> --file /dev/stdin
|
||||
talosctl --talosconfig talos/talosconfig config endpoint <node-ip>
|
||||
talosctl --talosconfig talos/talosconfig -n <node-ip> bootstrap
|
||||
talosctl --talosconfig talos/talosconfig -n <node-ip> kubeconfig
|
||||
```
|
||||
|
||||
Back up `controlplane.yaml` and `talosconfig` outside this folder: they are the only copy of
|
||||
the cluster PKI and admin credentials.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Single node: let workloads (sish) run on the control plane by dropping the
|
||||
# default control-plane NoSchedule taint.
|
||||
apiVersion: v1alpha1
|
||||
kind: KubeNodeConfig
|
||||
taints:
|
||||
node-role.kubernetes.io/control-plane:
|
||||
$patch: delete
|
||||
@@ -0,0 +1,61 @@
|
||||
# Ingress firewall: block everything that is not listed here. Loopback and
|
||||
# replies to outgoing connections are always allowed by Talos.
|
||||
#
|
||||
# Public TCP ports served by sish must match port-bind-range in
|
||||
# kubernetes/base/sish/config.yml (plus :80 HTTP and :2222 sish SSH).
|
||||
# Closed on purpose: flannel VXLAN 4789/udp (single node, unauthenticated),
|
||||
# etcd 2379-2383, kubelet 10250, kube-proxy 10256, trustd 50001 (only needed
|
||||
# when other nodes join).
|
||||
apiVersion: v1alpha1
|
||||
kind: NetworkDefaultActionConfig
|
||||
ingress: block
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: NetworkRuleConfig
|
||||
name: sish-public
|
||||
portSelector:
|
||||
ports:
|
||||
- 22 # gitea ssh (raw tcp forward)
|
||||
- 80 # sish http, routed by Host header
|
||||
- 443 # sish tls, routed by SNI
|
||||
- 2222 # sish ssh endpoint for connectors (public key auth)
|
||||
- 20000-20099 # reserved for raw tcp forwards
|
||||
protocol: tcp
|
||||
ingress:
|
||||
- subnet: 0.0.0.0/0
|
||||
- subnet: ::/0
|
||||
---
|
||||
# Talos API (apid) and Kubernetes API, both mTLS / client-cert authenticated
|
||||
apiVersion: v1alpha1
|
||||
kind: NetworkRuleConfig
|
||||
name: talos-and-kube-api
|
||||
portSelector:
|
||||
ports:
|
||||
- 50000
|
||||
- 6443
|
||||
protocol: tcp
|
||||
ingress:
|
||||
- subnet: 0.0.0.0/0
|
||||
- subnet: ::/0
|
||||
---
|
||||
# CoreDNS forwards to the Talos host DNS (forwardKubeDNSToHost), which pods reach
|
||||
# on the host, so the pod network needs DNS to the node
|
||||
apiVersion: v1alpha1
|
||||
kind: NetworkRuleConfig
|
||||
name: pod-dns
|
||||
portSelector:
|
||||
ports:
|
||||
- 53
|
||||
protocol: udp
|
||||
ingress:
|
||||
- subnet: 10.244.0.0/16
|
||||
---
|
||||
apiVersion: v1alpha1
|
||||
kind: NetworkRuleConfig
|
||||
name: pod-dns-tcp
|
||||
portSelector:
|
||||
ports:
|
||||
- 53
|
||||
protocol: tcp
|
||||
ingress:
|
||||
- subnet: 10.244.0.0/16
|
||||
@@ -0,0 +1,6 @@
|
||||
# Lets non-root processes bind ports >= 22, so sish (hostNetwork, uid 65534,
|
||||
# no capabilities) can listen on :22 (Gitea SSH), :80 and :443.
|
||||
# The edge runs nothing else that could grab 22-79.
|
||||
machine:
|
||||
sysctls:
|
||||
net.ipv4.ip_unprivileged_port_start: "22"
|
||||
Reference in New Issue
Block a user