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
|
- explicit SSH inventory for Debian, RPi, Pimox, and OCI edge
|
||||||
- repo-managed Tailscale ACLs
|
- 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
|
The workflow only blocks automatic deploy for external Gitea service
|
||||||
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
||||||
`install_gitea_backup_timer`, `backup_gitea`, or `drill_gitea_restore`
|
`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
|
- explicit SSH inventory for Debian, RPi, Pimox, and OCI edge
|
||||||
- repo-managed Tailscale ACLs
|
- 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
|
The workflow only blocks automatic deploy for external Gitea service
|
||||||
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
changes: files under `infra/gitea/`, or edits inside the `deploy_gitea`,
|
||||||
`install_gitea_backup_timer`, `backup_gitea`, or `drill_gitea_restore`
|
`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
|
self_heal = true
|
||||||
create_namespace = 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 = {
|
website-production = {
|
||||||
project = "default"
|
project = "default"
|
||||||
path = "apps/website"
|
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
|
Tailscale ACL validation, Kubernetes RBAC, automation service accounts, and the
|
||||||
Gitea runner.
|
Gitea runner.
|
||||||
|
|
||||||
|
`kubeconfig-readonly`
|
||||||
|
: Generate a separate local read-only kubeconfig from the repo-managed
|
||||||
|
`homelab-access/homelab-readonly` ServiceAccount.
|
||||||
|
|
||||||
`nuke`
|
`nuke`
|
||||||
: Destroy Kubernetes state and Pimox worker VMs. Requires
|
: Destroy Kubernetes state and Pimox worker VMs. Requires
|
||||||
`LAB_CONFIRM_NUKE=homelab`.
|
`LAB_CONFIRM_NUKE=homelab`.
|
||||||
|
|
|
||||||
10
jeannie
10
jeannie
|
|
@ -5957,6 +5957,11 @@ drill_pihole_restore() {
|
||||||
"${REPO_ROOT}/scripts/restore-drill" pihole
|
"${REPO_ROOT}/scripts/restore-drill" pihole
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kubeconfig_readonly() {
|
||||||
|
require_debian_server "kubeconfig-readonly"
|
||||||
|
"${REPO_ROOT}/scripts/kubeconfig-readonly"
|
||||||
|
}
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
up)
|
up)
|
||||||
up
|
up
|
||||||
|
|
@ -5982,6 +5987,9 @@ case "${1:-}" in
|
||||||
access-audit)
|
access-audit)
|
||||||
access_audit
|
access_audit
|
||||||
;;
|
;;
|
||||||
|
kubeconfig-readonly)
|
||||||
|
kubeconfig_readonly
|
||||||
|
;;
|
||||||
apps)
|
apps)
|
||||||
require_debian_server "apps"
|
require_debian_server "apps"
|
||||||
jeannie_log_start "apps"
|
jeannie_log_start "apps"
|
||||||
|
|
@ -6117,7 +6125,7 @@ case "${1:-}" in
|
||||||
echo "Log: ${JEANNIE_LOG_FILE}"
|
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
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
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