mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-21 19:56:33 +00:00
bfacca7789
* feat(eks): 6c AccessEntry CRUD + access-policy association
Replace the nine notImpl() AccessEntry/AccessPolicy stubs with real
KV-backed handlers (Q9, API auth mode):
- AccessEntry CRUD persisted at clusters/{name}/access-entries/{hash};
the principal ARN is hashed into the key (ARNs carry ':', illegal in
NATS KV keys) while the record keeps the plaintext ARN.
- AssociateAccessPolicy/Disassociate/ListAssociated against the four
supported AWS-managed policies; unsupported policy ARN or bad access
scope -> InvalidParameterValue.
- ListAccessPolicies returns the static supported catalogue.
- Cluster-existence gate -> ResourceNotFoundException; duplicate create
-> ResourceInUseException; node-type entries rejected until 6d.
Updates the daemon routing test for the now-implemented handlers.
* feat(eks): plumb caller principal ARN + bootstrap-creator-admin
Thread the caller's resolved IAM principal ARN from the gateway to the
EKS control plane via a new X-Principal-ARN NATS header, so CreateCluster
can mint the bootstrapClusterCreatorAdminPermissions AccessEntry (Q9):
- utils.NATSRequest takes optional extra headers; PrincipalARNHeader +
PrincipalARNFromMsg added.
- Gateway resolves the caller ARN best-effort (buildCallerARN) and passes
it down the EKS route table; only CreateCluster consumes it.
- CreateCluster signature gains callerPrincipalARN; default-true bootstrap
flag mints a system:masters entry keyed by the caller's exact ARN (the
token webhook resolves the same ARN from STS). '' degrades gracefully.
- Dedicated daemon handler reads both headers.
* feat(eks): IAM token webhook + STS verify-over-NATS + apiserver wiring
The eks-token-webhook (baked into the eks-server AMI) authenticates kubectl
requests that present an `aws eks get-token` bearer token: decode the presigned
STS GetCallerIdentity URL, verify it, resolve the principal ARN to its cluster
AccessEntry, and answer the apiserver TokenReview with the mapped username +
groups.
STS is hosted in-process in the awsgw service and is not otherwise on the bus,
so awsgw now serves an `eks.VerifyToken` NATS subject that wraps
VerifyPresignedGetCallerIdentity (the cluster name is forwarded so STS enforces
the x-k8s-aws-id anti-replay pin). The webhook reads the AccessEntry KV directly
over its own NATS connection.
Apiserver wiring: the k3s config skeleton sets
authentication-token-webhook-config-file; the webhook writes its own self-signed
serving cert + the apiserver kubeconfig at startup (ordered `before k3s`, with a
bounded wait in the k3s start_pre so the apiserver never loads a missing file).
* fix(eks): omit empty spec from token webhook TokenReview response
* fix(sts): presigned GetCallerIdentity must hash empty body, not UNSIGNED-PAYLOAD
VerifyPresignedGetCallerIdentity built the SigV4 canonical request with
"UNSIGNED-PAYLOAD" as the payload hash. That is an S3-only convention; botocore
and the aws-sdk presign non-S3 requests (sts:GetCallerIdentity, the shape `aws
eks get-token` produces) with the SHA256 of the empty string. Every real
get-token therefore failed signature verification with InvalidIdentityToken,
breaking the entire EKS IAM token-auth flow end to end.
Use the empty-string SHA256. The existing hand-rolled presign tests masked the
bug by signing with the same wrong constant; they now sign correctly, and a new
test presigns with the real aws-sdk-go v4 signer (the same canonicalisation the
CLI uses) so this can't regress silently.
Verified live: a real `aws eks get-token --cluster-name dev1` now resolves to
the caller principal over the awsgw eks.VerifyToken surface; an otherwise-valid
token presented for a different cluster is still rejected (anti-replay intact).
* fix(eks): match access-entry routes on escaped path so slashed principal ARNs resolve
IAM principal ARNs (user/admin, role/foo) embed a '/' that the AWS CLI
percent-encodes in the request path. The dispatcher matched r.URL.Path
(decoded), so %2F became a literal '/' and split the ARN across the
([^/]+) capture, dropping every describe/update/delete-access-entry and
associate/disassociate/list-access-policy call to InvalidActionException.
Match r.URL.EscapedPath() so the encoded ARN stays one segment, then
url.PathUnescape each captured param before it reaches the handler.
* test(eks): unit-cover access-entry scope validation, AWS converters, store guards
50 lines
1.9 KiB
Plaintext
50 lines
1.9 KiB
Plaintext
#!/sbin/openrc-run
|
|
# K3s server control plane. v1 = single-node embedded SQLite (kine) datastore;
|
|
# the per-cluster config.yaml omits cluster-init so k3s uses SQLite, not etcd
|
|
# (etcd's per-commit fsync stalls on the viperblock root volume). Data lives at
|
|
# /var/lib/rancher/k3s; subsequent starts reuse it. Multi-server HA needs etcd
|
|
# and is deferred.
|
|
|
|
description="K3s Kubernetes server (Mulga EKS control plane)"
|
|
command="/usr/local/bin/k3s"
|
|
command_args="server --config /etc/rancher/k3s/config.yaml"
|
|
command_background="yes"
|
|
pidfile="/run/k3s.pid"
|
|
output_log="/var/log/k3s.log"
|
|
error_log="/var/log/k3s.log"
|
|
|
|
# Generous start timeout — K3s bootstraps containerd, initialises the SQLite
|
|
# datastore, pulls system images. First boot can take 2-3 minutes on cold cache.
|
|
start_stop_daemon_args="--wait 5000"
|
|
|
|
depend() {
|
|
need net cgroups
|
|
after firewall
|
|
}
|
|
|
|
start_pre() {
|
|
# config.yaml is written by k3s-first-boot from the skeleton + cloud-init
|
|
# data. Refuse to start if neither exists.
|
|
if [ ! -f /etc/rancher/k3s/config.yaml ]; then
|
|
if [ -f /etc/rancher/k3s/config.yaml.skel ]; then
|
|
cp /etc/rancher/k3s/config.yaml.skel /etc/rancher/k3s/config.yaml
|
|
else
|
|
eerror "no /etc/rancher/k3s/config.yaml and no .skel to derive from"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# The apiserver is configured with --authentication-token-webhook-config-file
|
|
# pointing at the kubeconfig eks-token-webhook writes at startup. `before k3s`
|
|
# orders the webhook's start but it backgrounds, so wait (bounded) for the
|
|
# kubeconfig to land before letting the apiserver load a missing file.
|
|
i=0
|
|
while [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ] && [ "${i}" -lt 30 ]; do
|
|
sleep 1
|
|
i=$((i + 1))
|
|
done
|
|
if [ ! -f /etc/spinifex-eks/token-webhook.kubeconfig ]; then
|
|
ewarn "eks-token-webhook kubeconfig absent after ${i}s; IAM token auth will be unavailable until the webhook starts"
|
|
fi
|
|
}
|