#!/usr/bin/env bash set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" KUBECONFIG="${KUBECONFIG:-${HOME}/.kube/config}" section() { printf '\n== %s ==\n' "$1" } warn_file_mode() { local path="$1" local mode if [[ ! -e "${path}" ]]; then printf '%-34s missing - %s\n' "key file" "${path}" return 0 fi mode="$(stat -c '%a' "${path}" 2>/dev/null || stat -f '%Lp' "${path}" 2>/dev/null || true)" printf '%-34s %s mode=%s\n' "key file" "${path}" "${mode:-unknown}" case "${mode}" in 600 | 400) ;; *) printf ' warn: private SSH keys should normally be 0600 or 0400\n' ;; esac } ssh_inventory() { section "SSH Inventory" printf '%-18s %s@%s key=%s\n' "Debian" "${USER:-jv}" "${LAB_DEBIAN_HOST:-${LAB_GITEA_HOST:-192.168.100.73}}" "${LAB_DEBIAN_SSH_KEY_PATH:-${HOME}/.ssh/id_ed25519}" printf '%-18s %s@%s key=%s\n' "RPi4" "${LAB_RPI_USER:-${LAB_RASPBERRY_USER:-jv}}" "${LAB_RPI_HOST:-${LAB_RASPBERRY_HOST:-192.168.100.89}}" "${LAB_RPI_SSH_KEY_PATH:-${LAB_RASPBERRY_SSH_KEY_PATH:-${HOME}/.ssh/id_ed25519}}" printf '%-18s %s@%s key=%s\n' "Pimox" "${LAB_PIMOX_USER:-${TF_VAR_pimox_user:-jv}}" "${LAB_PIMOX_HOST:-${TF_VAR_pimox_host:-192.168.100.80}}" "${LAB_PIMOX_SSH_KEY_PATH:-${TF_VAR_pimox_ssh_key_path:-${HOME}/.ssh/id_ed25519}}" printf '%-18s %s@%s\n' "OCI edge" "${LAB_EDGE_USER:-ubuntu}" "${LAB_EDGE_HOST:-132.145.170.74}" warn_file_mode "${LAB_DEBIAN_SSH_KEY_PATH:-${HOME}/.ssh/id_ed25519}" } git_access() { section "Git Remotes" git -C "${REPO_ROOT}" remote -v || true printf '\nCurrent branch:\n' git -C "${REPO_ROOT}" branch -vv || true printf '\nGitea deploy key expected path: %s\n' "${LAB_GITEA_REPO_SSH_KEY_PATH:-${HOME}/.ssh/id_ed25519.pub}" printf 'Gitea deploy key title: %s\n' "${LAB_GITEA_REPO_DEPLOY_KEY_TITLE:-debian-host-my-homelab-configs}" printf 'Gitea deploy key read-only: %s\n' "${LAB_GITEA_REPO_DEPLOY_KEY_READ_ONLY:-false}" if [[ "${LAB_GITEA_REPO_DEPLOY_KEY_READ_ONLY:-false}" != "true" ]]; then printf ' warn: deploy key is expected to be write-capable for Actions/deploy mirroring\n' fi } tailnet_access() { section "Tailscale" if command -v tailscale >/dev/null 2>&1; then tailscale ip -4 || true tailscale status || true else printf 'tailscale command not installed\n' fi printf '\nRepo-managed ACL validation:\n' "${REPO_ROOT}/scripts/validate-tailnet-policy" || true } kubernetes_access() { section "Kubernetes Access" if ! command -v kubectl >/dev/null 2>&1; then printf 'kubectl not installed\n' return 0 fi if [[ ! -s "${KUBECONFIG}" ]]; then printf 'kubeconfig missing: %s\n' "${KUBECONFIG}" return 0 fi printf 'KUBECONFIG: %s\n' "${KUBECONFIG}" kubectl --kubeconfig "${KUBECONFIG}" config current-context 2>/dev/null || true printf '\nCan current user perform broad actions?\n' for verb in get list create update delete; do printf '%-10s ' "${verb}" kubectl --kubeconfig "${KUBECONFIG}" auth can-i "${verb}" pods --all-namespaces 2>/dev/null || true done printf '\nClusterRoleBindings to cluster-admin:\n' kubectl --kubeconfig "${KUBECONFIG}" get clusterrolebinding -o jsonpath='{range .items[?(@.roleRef.name=="cluster-admin")]}{.metadata.name}{" -> "}{range .subjects[*]}{.kind}{"/"}{.namespace}{"/"}{.name}{" "}{end}{"\n"}{end}' 2>/dev/null || true printf '\nAutomation service accounts:\n' kubectl --kubeconfig "${KUBECONFIG}" get serviceaccounts -A 2>/dev/null | awk 'NR == 1 || /argocd|gitea|runner|kyverno|tetragon|prometheus|jeannie|homelab/' cat <<'EOF' Read-only kubeconfig target: - Create a dedicated read-only ClusterRole/Binding for dashboards and audits. - Keep admin kubeconfig only on the Debian control host. - Use separate service accounts for automation instead of reusing admin. EOF } runner_access() { section "Automation Runners" if command -v systemctl >/dev/null 2>&1; then systemctl is-active homelab-gitea-runner.service 2>/dev/null || true systemctl status homelab-gitea-runner.service --no-pager -l 2>/dev/null | sed -n '1,12p' || true else printf 'systemctl unavailable\n' fi } main() { ssh_inventory git_access tailnet_access kubernetes_access runner_access } main "$@"