Add read-only Kubernetes access model
This commit is contained in:
parent
e10be77cc2
commit
1d553771f0
|
|
@ -813,6 +813,13 @@ separate identities:
|
|||
- explicit SSH inventory for Debian, RPi, Pimox, and OCI edge
|
||||
- repo-managed Tailscale ACLs
|
||||
|
||||
The read-only Kubernetes identity is managed in `apps/access-control`. After
|
||||
that app syncs, generate a separate read-only kubeconfig on the Debian host:
|
||||
|
||||
```bash
|
||||
./jeannie kubeconfig-readonly
|
||||
```
|
||||
|
||||
The workflow only blocks automatic deploy for external Gitea service
|
||||
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
||||
`install_gitea_backup_timer`, `backup_gitea`, or `drill_gitea_restore`
|
||||
|
|
|
|||
|
|
@ -813,6 +813,13 @@ separate identities:
|
|||
- explicit SSH inventory for Debian, RPi, Pimox, and OCI edge
|
||||
- repo-managed Tailscale ACLs
|
||||
|
||||
The read-only Kubernetes identity is managed in `apps/access-control`. After
|
||||
that app syncs, generate a separate read-only kubeconfig on the Debian host:
|
||||
|
||||
```bash
|
||||
./{{ main_script }} kubeconfig-readonly
|
||||
```
|
||||
|
||||
The workflow only blocks automatic deploy for external Gitea service
|
||||
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
||||
`install_gitea_backup_timer`, `backup_gitea`, or `drill_gitea_restore`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- readonly-rbac.yaml
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: homelab-access
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: homelab-readonly
|
||||
namespace: homelab-access
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: homelab-readonly
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- componentstatuses
|
||||
- configmaps
|
||||
- endpoints
|
||||
- events
|
||||
- namespaces
|
||||
- nodes
|
||||
- persistentvolumeclaims
|
||||
- persistentvolumes
|
||||
- pods
|
||||
- pods/log
|
||||
- replicationcontrollers
|
||||
- resourcequotas
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
- replicasets
|
||||
- statefulsets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- cronjobs
|
||||
- jobs
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
resources:
|
||||
- ingresses
|
||||
- networkpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- monitoring.coreos.com
|
||||
resources:
|
||||
- prometheusrules
|
||||
- servicemonitors
|
||||
- podmonitors
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- argoproj.io
|
||||
resources:
|
||||
- applications
|
||||
- appprojects
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kyverno.io
|
||||
- policies.kyverno.io
|
||||
resources:
|
||||
- clusterpolicies
|
||||
- policies
|
||||
- imagevalidatingpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: homelab-readonly
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: homelab-readonly
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: homelab-readonly
|
||||
namespace: homelab-access
|
||||
|
|
@ -39,6 +39,15 @@ variable "applications" {
|
|||
self_heal = true
|
||||
create_namespace = true
|
||||
}
|
||||
access-control = {
|
||||
project = "default"
|
||||
path = "apps/access-control"
|
||||
namespace = "homelab-access"
|
||||
target_revision = "main"
|
||||
prune = true
|
||||
self_heal = true
|
||||
create_namespace = true
|
||||
}
|
||||
website-production = {
|
||||
project = "default"
|
||||
path = "apps/website"
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ formatting, tailnet policy validation, and optional static security checks.
|
|||
Tailscale ACL validation, Kubernetes RBAC, automation service accounts, and the
|
||||
Gitea runner.
|
||||
|
||||
`kubeconfig-readonly`
|
||||
: Generate a separate local read-only kubeconfig from the repo-managed
|
||||
`homelab-access/homelab-readonly` ServiceAccount.
|
||||
|
||||
`nuke`
|
||||
: Destroy Kubernetes state and Pimox worker VMs. Requires
|
||||
`LAB_CONFIRM_NUKE=homelab`.
|
||||
|
|
|
|||
10
jeannie
10
jeannie
|
|
@ -5957,6 +5957,11 @@ drill_pihole_restore() {
|
|||
"${REPO_ROOT}/scripts/restore-drill" pihole
|
||||
}
|
||||
|
||||
kubeconfig_readonly() {
|
||||
require_debian_server "kubeconfig-readonly"
|
||||
"${REPO_ROOT}/scripts/kubeconfig-readonly"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
up)
|
||||
up
|
||||
|
|
@ -5982,6 +5987,9 @@ case "${1:-}" in
|
|||
access-audit)
|
||||
access_audit
|
||||
;;
|
||||
kubeconfig-readonly)
|
||||
kubeconfig_readonly
|
||||
;;
|
||||
apps)
|
||||
require_debian_server "apps"
|
||||
jeannie_log_start "apps"
|
||||
|
|
@ -6117,7 +6125,7 @@ case "${1:-}" in
|
|||
echo "Log: ${JEANNIE_LOG_FILE}"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|validate|access-audit|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}"
|
||||
echo "Usage: $0 {up|plan [all|provisioning|cluster|platform|apps|edge]|rebuild-cluster|stop-cluster|start-cluster|status|validate|access-audit|kubeconfig-readonly|apps|website-translation-model|website-ollama-listen|ollama-setup|deploy-gitea|rpi-services|bootstrap-gitea-repo|backup-gitea|drill-restore|drill-gitea-restore|drill-pihole-restore|install-gitea-runner|move-prometheus-stack-workers|doctor-versions|doctor-edge|doctor-gitea|doctor-rpi|doctor-cluster|preflight|doctor-preapply|inventory-check|state-backup|fix-debian-docker-root|secrets-init|secrets-check|tailnet-policy-check|ai-index|ai-check|security-scan|security-prepare|security-zap|security-k8s|security-host|security-trivy|security-secrets|security-nuclei|security-web|security-logs|security-runtime|security-attack-path|openwrt|nuke}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ADMIN_KUBECONFIG="${KUBECONFIG:-${KUBECONFIG_PATH:-${HOME}/.kube/config}}"
|
||||
READONLY_NAMESPACE="${READONLY_NAMESPACE:-homelab-access}"
|
||||
READONLY_SERVICE_ACCOUNT="${READONLY_SERVICE_ACCOUNT:-homelab-readonly}"
|
||||
READONLY_KUBECONFIG="${READONLY_KUBECONFIG:-${HOME}/.kube/homelab-readonly.conf}"
|
||||
READONLY_TOKEN_DURATION="${READONLY_TOKEN_DURATION:-24h}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: scripts/kubeconfig-readonly
|
||||
|
||||
Generate a separate read-only kubeconfig from the repo-managed
|
||||
homelab-access/homelab-readonly ServiceAccount.
|
||||
EOF
|
||||
}
|
||||
|
||||
require_kubectl() {
|
||||
if ! command -v kubectl >/dev/null 2>&1; then
|
||||
echo "kubectl is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -s "${ADMIN_KUBECONFIG}" ]]; then
|
||||
echo "admin kubeconfig missing: ${ADMIN_KUBECONFIG}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local server
|
||||
local cluster_name
|
||||
local ca_data
|
||||
local token
|
||||
local user_name="homelab-readonly"
|
||||
local context_name="homelab-readonly"
|
||||
|
||||
case "${1:-}" in
|
||||
-h | --help | help)
|
||||
usage
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
require_kubectl
|
||||
|
||||
kubectl --kubeconfig "${ADMIN_KUBECONFIG}" -n "${READONLY_NAMESPACE}" get serviceaccount "${READONLY_SERVICE_ACCOUNT}" >/dev/null
|
||||
token="$(kubectl --kubeconfig "${ADMIN_KUBECONFIG}" -n "${READONLY_NAMESPACE}" create token "${READONLY_SERVICE_ACCOUNT}" --duration="${READONLY_TOKEN_DURATION}")"
|
||||
server="$(kubectl --kubeconfig "${ADMIN_KUBECONFIG}" config view --minify -o jsonpath='{.clusters[0].cluster.server}')"
|
||||
cluster_name="$(kubectl --kubeconfig "${ADMIN_KUBECONFIG}" config view --minify -o jsonpath='{.clusters[0].name}')"
|
||||
ca_data="$(kubectl --kubeconfig "${ADMIN_KUBECONFIG}" config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')"
|
||||
|
||||
mkdir -p "$(dirname "${READONLY_KUBECONFIG}")"
|
||||
umask 077
|
||||
cat >"${READONLY_KUBECONFIG}" <<EOF
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: ${cluster_name}
|
||||
cluster:
|
||||
server: ${server}
|
||||
certificate-authority-data: ${ca_data}
|
||||
users:
|
||||
- name: ${user_name}
|
||||
user:
|
||||
token: ${token}
|
||||
contexts:
|
||||
- name: ${context_name}
|
||||
context:
|
||||
cluster: ${cluster_name}
|
||||
user: ${user_name}
|
||||
namespace: default
|
||||
current-context: ${context_name}
|
||||
EOF
|
||||
chmod 0600 "${READONLY_KUBECONFIG}"
|
||||
|
||||
echo "Created read-only kubeconfig: ${READONLY_KUBECONFIG}"
|
||||
echo "Test with:"
|
||||
echo " KUBECONFIG=${READONLY_KUBECONFIG} kubectl get pods -A"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Reference in New Issue