Initial commit

This commit is contained in:
Scooby Husky
2026-03-09 20:21:35 -05:00
commit aacb8eebbe
314 changed files with 21766 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
set -euo pipefail
# CRD versions (override via env vars for upgrades)
PROMETHEUS_OPERATOR_VERSION="${PROMETHEUS_OPERATOR_VERSION:-v0.76.0}"
GATEWAY_API_VERSION="${GATEWAY_API_VERSION:-v1.2.0}"
echo "=== Bootstrap: Installing required CRDs ==="
# Install Prometheus Operator CRDs (required before helmfile diff can run)
echo "Installing Prometheus Operator CRDs (${PROMETHEUS_OPERATOR_VERSION})..."
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_prometheuses.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_prometheusrules.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagers.yaml
kubectl apply --server-side -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/${PROMETHEUS_OPERATOR_VERSION}/example/prometheus-operator-crd/monitoring.coreos.com_alertmanagerconfigs.yaml
echo "✅ Prometheus Operator CRDs installed successfully"
# Install Kyverno CRDs (required for PolicyReport resources)
echo ""
echo "Installing Kyverno PolicyReport CRDs..."
kubectl apply --server-side -f https://raw.githubusercontent.com/kubernetes-sigs/wg-policy-prototypes/master/policy-report/crd/v1beta1/wgpolicyk8s.io_clusterpolicyreports.yaml 2>/dev/null || echo "PolicyReport CRDs may already exist or URL changed - Kyverno will install them"
kubectl apply --server-side -f https://raw.githubusercontent.com/kubernetes-sigs/wg-policy-prototypes/master/policy-report/crd/v1beta1/wgpolicyk8s.io_policyreports.yaml 2>/dev/null || echo "PolicyReport CRDs may already exist or URL changed - Kyverno will install them"
echo "✅ Kyverno PolicyReport CRDs installed (or will be installed by Kyverno chart)"
# Install Gateway API CRDs (required for Istio ambient waypoint proxies)
echo ""
echo "Installing Gateway API CRDs (required for Istio ambient waypoint proxies)..."
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/${GATEWAY_API_VERSION}/standard-install.yaml
echo "✅ Gateway API CRDs installed successfully"
+72
View File
@@ -0,0 +1,72 @@
#!/bin/bash
# Cilium Bootstrap - Install Cilium CNI before helmfile
# Nodes require a CNI to become Ready. This script installs Cilium via Helm
# and applies L2 announcement policies (replacing MetalLB).
#
# Usage: ./scripts/cilium-bootstrap.sh
set -euo pipefail
echo "================================================="
echo "Cilium CNI Bootstrap"
echo "================================================="
echo ""
# Add Cilium Helm repo
echo "Adding Cilium Helm repository..."
helm repo add cilium https://helm.cilium.io 2>/dev/null || true
helm repo update cilium
# Check if Cilium is already installed
if helm -n kube-system status cilium >/dev/null 2>&1; then
echo "✅ Cilium is already installed"
echo ""
echo "Upgrading Cilium to match helmfile values..."
helm upgrade cilium cilium/cilium \
--namespace kube-system \
--values infrastructure/cilium/values.yaml \
--wait --timeout 300s
else
echo "Installing Cilium CNI..."
helm install cilium cilium/cilium \
--namespace kube-system \
--values infrastructure/cilium/values.yaml \
--wait --timeout 300s
fi
echo ""
echo "Waiting for Cilium to be ready..."
kubectl -n kube-system rollout status daemonset/cilium --timeout=300s
echo ""
echo "Applying L2 announcement policy and IP pool..."
kubectl apply -f infrastructure/cilium/l2-announcement-policy.yaml
echo ""
echo "Applying baseline network policies..."
kubectl apply -f infrastructure/cilium/network-policies/dns.yaml
# Apply per-namespace policies only if namespaces exist
for ns in vault authentik rook-ceph; do
if kubectl get ns "$ns" >/dev/null 2>&1; then
kubectl apply -f infrastructure/cilium/network-policies/baseline.yaml 2>/dev/null || true
break
fi
done
echo ""
echo "Verifying Cilium status..."
kubectl -n kube-system exec ds/cilium -- cilium status --brief 2>/dev/null || echo " (cilium CLI check will be available after pods stabilize)"
echo ""
echo "Checking node readiness..."
kubectl get nodes -o wide
echo ""
echo "Verifying L2 announcements..."
kubectl get ciliuml2announcementpolicy,ciliumloadbalancerippool 2>/dev/null || echo " (L2 CRDs being registered...)"
echo ""
echo "================================================="
echo "✅ Cilium CNI bootstrap complete"
echo "================================================="
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
# scripts/fix-gitlab-auth.sh
# Fix GitLab authentication issues by refreshing passwords
set -euo pipefail
echo "=== GitLab Authentication Fix ==="
echo "This script fixes the 500 errors caused by password synchronization issues"
# Function to restart deployment and wait
restart_deployment() {
local deployment=$1
local namespace=$2
echo "Restarting $deployment in $namespace..."
kubectl rollout restart deployment/$deployment -n $namespace
kubectl rollout status deployment/$deployment -n $namespace --timeout=120s
}
# 1. Refresh database secrets
echo "Step 1: Refreshing database secrets..."
kubectl delete secret pg-gitlab-app -n gitlab --ignore-not-found=true
echo "Waiting for database secret to be recreated..."
for i in {1..30}; do
if kubectl -n gitlab get secret pg-gitlab-app >/dev/null 2>&1; then
echo "✓ Database secret recreated"
break
fi
echo " waiting... (attempt $i/30)"
sleep 2
done
# 2. Refresh Redis secrets
echo "Step 2: Refreshing Redis secrets..."
kubectl delete secret redis-gitlab-secret -n gitlab --ignore-not-found=true
echo "Waiting for Redis secret to be recreated..."
for i in {1..30}; do
if kubectl -n gitlab get secret redis-gitlab-secret >/dev/null 2>&1; then
echo "✓ Redis secret recreated"
break
fi
echo " waiting... (attempt $i/30)"
sleep 2
done
# 3. Trigger PostgreSQL reload
echo "Step 3: Reloading PostgreSQL clusters..."
kubectl annotate cluster pg-gitlab -n gitlab cnpg.io/reload=$(date +%s) --overwrite
kubectl annotate cluster pg-praefect -n gitlab cnpg.io/reload=$(date +%s) --overwrite
# 4. Restart Redis
echo "Step 4: Restarting Redis..."
kubectl rollout restart statefulset/redis-gitlab -n gitlab
kubectl rollout status statefulset/redis-gitlab -n gitlab --timeout=120s
# 5. Restart GitLab services
echo "Step 5: Restarting GitLab services..."
restart_deployment "gitlab-webservice-default" "gitlab"
restart_deployment "gitlab-sidekiq-all-in-1-v2" "gitlab"
restart_deployment "gitlab-toolbox" "gitlab"
# 6. Verify GitLab is working
echo "Step 6: Verifying GitLab functionality..."
sleep 30
# Test GitLab health endpoint
for i in {1..10}; do
HTTP_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" https://gitlab.kube.huskypup.net/-/readiness)
if [ "$HTTP_CODE" = "200" ]; then
echo "✓ GitLab is responding correctly"
break
fi
echo " waiting for GitLab to be ready... (attempt $i/10) - HTTP $HTTP_CODE"
sleep 10
done
# Test main page
HTTP_CODE=$(curl -k -s -o /dev/null -w "%{http_code}" https://gitlab.kube.huskypup.net)
if [ "$HTTP_CODE" = "302" ]; then
echo "✓ GitLab main page is working (redirecting to login as expected)"
elif [ "$HTTP_CODE" = "200" ]; then
echo "✓ GitLab main page is working"
else
echo "⚠ GitLab main page returned HTTP $HTTP_CODE (may still be starting)"
fi
echo ""
echo "===================================================================="
echo "GitLab Authentication Fix Complete"
echo "===================================================================="
echo "✓ Database secrets refreshed"
echo "✓ Redis secrets refreshed"
echo "✓ PostgreSQL clusters reloaded"
echo "✓ Redis restarted"
echo "✓ GitLab services restarted"
echo ""
echo "GitLab should now be accessible at: https://gitlab.kube.huskypup.net"
echo "If you still see 500 errors, check the logs with:"
echo " kubectl logs -n gitlab -l app=webservice --tail=20"
echo "===================================================================="
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# gitlab-add-hostaliases.sh
# Add hostAliases to GitLab deployments for Authentik OIDC
set -euo pipefail
echo "=== Adding hostAliases to GitLab deployments ==="
NS=gitlab
# Route via Istio ingressgateway LoadBalancer (ingress-nginx removed)
EDGE_IP=$(kubectl get svc -n istio-system istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Istio edge gateway IP: $EDGE_IP"
# Check if webservice deployment already has hostAliases
if kubectl get deployment gitlab-webservice-default -n "${NS}" -o jsonpath='{.spec.template.spec.hostAliases}' | grep -q "${EDGE_IP}"; then
echo "✅ webservice already has hostAliases configured"
else
echo "Adding hostAliases to webservice..."
kubectl patch deployment gitlab-webservice-default -n "${NS}" --type='json' -p="[{\"op\": \"add\", \"path\": \"/spec/template/spec/hostAliases\", \"value\": [{\"ip\": \"${EDGE_IP}\", \"hostnames\": [\"auth.kube.huskypup.net\"]}]}]"
echo "✅ hostAliases added to webservice"
fi
# Check if sidekiq deployment already has hostAliases
if kubectl get deployment gitlab-sidekiq-all-in-1-v2 -n "${NS}" -o jsonpath='{.spec.template.spec.hostAliases}' | grep -q "${EDGE_IP}"; then
echo "✅ sidekiq already has hostAliases configured"
else
echo "Adding hostAliases to sidekiq..."
kubectl patch deployment gitlab-sidekiq-all-in-1-v2 -n "${NS}" --type='json' -p="[{\"op\": \"add\", \"path\": \"/spec/template/spec/hostAliases\", \"value\": [{\"ip\": \"${EDGE_IP}\", \"hostnames\": [\"auth.kube.huskypup.net\"]}]}]"
echo "✅ hostAliases added to sidekiq"
fi
echo ""
echo "=== hostAliases configuration complete ==="
echo "GitLab will now route auth.kube.huskypup.net through Istio edge gateway (${EDGE_IP})"
echo "This ensures proper SSL certificate validation for Authentik OIDC"
+354
View File
@@ -0,0 +1,354 @@
#!/bin/bash
# scripts/gitlab-bootstrap.sh
# GitLab presync bootstrap script - fully automated, no manual interaction required
# Auto-creates OAuth provider in Authentik and syncs credentials to GitLab
set -euo pipefail
echo "=== GitLab Bootstrap - Fully Automated with Authentik Integration ==="
echo "Setting up GitLab infrastructure with auto-generated secrets..."
# Change to the apps directory for relative paths
cd "$(dirname "$0")/../apps" || exit 1
# Ensure namespace exists
kubectl get ns gitlab >/dev/null 2>&1 || kubectl create ns gitlab
# ============================================================================
# Step 1: Ensure Authentik has GitLab OAuth provider
# ============================================================================
echo ""
echo "Step 1: Configuring Authentik OAuth provider for GitLab..."
# Apply the Authentik blueprint (if not already applied)
kubectl apply -f ../infrastructure/authentik/gitlab-blueprint.yaml 2>/dev/null || true
# Give Authentik time to process the blueprint (it auto-discovers ConfigMaps with the label)
echo "Waiting for Authentik to process GitLab blueprint..."
sleep 10
# ============================================================================
# Step 2: Deploy PostgreSQL clusters
# ============================================================================
echo ""
echo "Step 2: Deploying PostgreSQL clusters..."
# Apply CloudNativePG PostgreSQL clusters
# NOTE: CNPG auto-generates database passwords in secrets like pg-gitlab-app
echo "Applying GitLab PostgreSQL CNPG cluster..."
kubectl apply -f gitlab/cnpg-cluster.yaml
echo "Applying Praefect PostgreSQL CNPG cluster..."
kubectl apply -f gitlab/praefect-cnpg-cluster.yaml
# Apply PgBouncer poolers
echo "Applying PgBouncer connection poolers..."
kubectl apply -f gitlab/pgbouncer-pooler.yaml
# Wait for PostgreSQL clusters to be ready
echo "Waiting for PostgreSQL clusters to be ready..."
for i in {1..60}; do
READY=$(kubectl -n gitlab get cluster pg-gitlab -o jsonpath='{.status.instances}' 2>/dev/null || echo "0")
if [ "$READY" -ge "1" ]; then
echo "GitLab PostgreSQL cluster has $READY instance(s) ready!"
break
fi
sleep 5
done
for i in {1..60}; do
READY=$(kubectl -n gitlab get cluster pg-praefect -o jsonpath='{.status.instances}' 2>/dev/null || echo "0")
if [ "$READY" -ge "1" ]; then
echo "Praefect PostgreSQL cluster has $READY instance(s) ready!"
break
fi
sleep 5
done
# ============================================================================
# Step 3: Deploy Redis
# ============================================================================
echo ""
echo "Step 3: Deploying Redis..."
# Apply Redis standalone instance and auto-restart automation
echo "Applying GitLab Redis standalone instance..."
kubectl apply -f gitlab/redis-cluster.yaml
echo "Applying GitLab Redis auto-restart automation..."
kubectl apply -f gitlab/redis-auto-restart.yaml
echo "Applying GitLab PostgreSQL auto-restart automation..."
kubectl apply -f gitlab/pg-auto-restart.yaml
# Verify auto-restart CronJobs were created
echo "Verifying auto-restart CronJobs..."
for i in {1..10}; do
REDIS_OK=false
PG_GITLAB_OK=false
PG_PRAEFECT_OK=false
if kubectl get cronjob -n gitlab redis-secret-monitor >/dev/null 2>&1; then
REDIS_OK=true
fi
if kubectl get cronjob -n gitlab pg-gitlab-secret-monitor >/dev/null 2>&1; then
PG_GITLAB_OK=true
fi
if kubectl get cronjob -n gitlab pg-praefect-secret-monitor >/dev/null 2>&1; then
PG_PRAEFECT_OK=true
fi
if $REDIS_OK && $PG_GITLAB_OK && $PG_PRAEFECT_OK; then
echo "✓ All auto-restart CronJobs are deployed!"
break
fi
echo " waiting for CronJobs to be created... (attempt $i/10)"
sleep 1
done
# Apply Redis Sentinel HA cluster
echo "Applying GitLab Redis Sentinel HA..."
kubectl apply -f gitlab/redis-sentinel-ha.yaml
# Wait for Redis to be ready
echo "Waiting for Redis Sentinel cluster to be ready..."
kubectl wait --for=condition=ready pod -n gitlab -l app=redis-gitlab-ha --timeout=120s 2>/dev/null || echo "Redis may still be starting..."
# ============================================================================
# Step 4: Set up PgBouncer authentication (SCRAM-SHA-256)
# ============================================================================
echo ""
echo "Step 4: Setting up PgBouncer authentication with SCRAM-SHA-256..."
# Get the primary PostgreSQL pod (read-write)
PRIMARY_POD=$(kubectl get pod -n gitlab -l cnpg.io/cluster=pg-gitlab,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$PRIMARY_POD" ]; then
echo "❌ ERROR: Could not find primary PostgreSQL pod"
exit 1
fi
echo "Using primary pod: $PRIMARY_POD"
# Create table for password hashes (needed for PgBouncer SCRAM-SHA-256 auth)
echo "Creating user passwords table for PgBouncer..."
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "CREATE TABLE IF NOT EXISTS public.user_passwords (usename text primary key, passwd text);"
# Create user_search function to return password hashes from the table
echo "Creating user_search function for SCRAM-SHA-256 authentication..."
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "DROP FUNCTION IF EXISTS public.user_search(text);"
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "CREATE FUNCTION public.user_search(uname text) RETURNS TABLE(usename text, passwd text) AS \$\$ SELECT usename, passwd FROM public.user_passwords WHERE usename = \$1; \$\$ LANGUAGE sql SECURITY DEFINER;"
# Wait for PgBouncer pooler to create its role
echo "Waiting for PgBouncer pooler pods to be ready..."
for i in {1..30}; do
POOLER_READY=$(kubectl get pods -n gitlab -l cnpg.io/poolerName=pgbouncer-gitlab --no-headers 2>/dev/null | grep -c Running || echo "0")
if [ "$POOLER_READY" -ge "1" ]; then
echo "✅ PgBouncer pooler pods are running"
break
fi
echo " waiting for pooler pods... (attempt $i/30)"
sleep 2
done
# Grant permissions to PgBouncer auth user (create role if it doesn't exist)
echo "Granting permissions to PgBouncer auth user..."
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'cnpg_pooler_pgbouncer') THEN CREATE ROLE cnpg_pooler_pgbouncer LOGIN; END IF; END \$\$;" 2>/dev/null || echo " ↳ Role may already exist"
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "GRANT EXECUTE ON FUNCTION public.user_search(text) TO cnpg_pooler_pgbouncer;" 2>/dev/null || echo " ↳ Permission may already be granted"
# Sync password hash from pg_authid to user_passwords table
# This ensures PgBouncer can authenticate using SCRAM-SHA-256
echo "Syncing password hash from pg_authid to user_passwords table..."
kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -c "INSERT INTO public.user_passwords (usename, passwd) SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'app' ON CONFLICT (usename) DO UPDATE SET passwd = EXCLUDED.passwd;"
# Verify the password hash was stored correctly
echo "Verifying password hash sync..."
HASH_COUNT=$(kubectl exec -n gitlab "$PRIMARY_POD" -- psql -U postgres -t -c "SELECT COUNT(*) FROM public.user_passwords WHERE usename = 'app' AND passwd LIKE 'SCRAM-SHA-256%';" | tr -d ' ')
if [ "$HASH_COUNT" = "1" ]; then
echo "✅ PgBouncer authentication configured with SCRAM-SHA-256"
else
echo "⚠️ WARNING: Password hash may not be correctly stored"
echo " PgBouncer authentication may fail - check user_passwords table"
fi
# ============================================================================
# Step 5: Configure OAuth/SAML credentials
# ============================================================================
echo ""
echo "Step 5: Configuring OAuth and SAML credentials..."
# Try to sync OAuth credentials from Authentik to Vault
echo "Attempting to sync OAuth credentials from Authentik..."
if bash ../scripts/sync-gitlab-oauth.sh 2>&1 | grep -q "Successfully stored"; then
echo "✓ Successfully synced OAuth credentials from Authentik to Vault"
OAUTH_SYNCED=true
else
echo "⚠ Could not sync OAuth from Authentik (provider may not be ready yet)"
echo " GitLab will use placeholder credentials - run './scripts/sync-gitlab-oauth.sh' later to enable SSO"
OAUTH_SYNCED=false
fi
# Apply External Secrets for GitLab OIDC (will sync from Vault if available)
echo "Applying GitLab OIDC External Secrets..."
kubectl apply -f gitlab/external-secret.yaml
# Apply External Secrets for GitLab SAML (optional)
echo "Applying GitLab SAML External Secrets..."
kubectl apply -f gitlab/external-secret-saml.yaml 2>/dev/null || true
# Wait for External Secret to sync (with timeout)
echo "Checking if GitLab OIDC External Secret can sync from Vault..."
OIDC_SYNCED=false
for i in {1..10}; do
STATUS=$(kubectl -n gitlab get externalsecret gitlab-oidc -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$STATUS" = "True" ]; then
echo "✓ GitLab OIDC External Secret synced from Vault!"
OIDC_SYNCED=true
break
fi
sleep 2
done
# Create placeholder OAuth credentials in Vault if they don't exist
echo "Checking if OAuth credentials exist in Vault..."
VAULT_POD=$(kubectl get pods -n vault -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
VAULT_TOKEN=$(kubectl get secret -n vault vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)
if ! kubectl exec -n vault "$VAULT_POD" -- env VAULT_TOKEN="$VAULT_TOKEN" vault kv get secret/gitlab-oauth >/dev/null 2>&1; then
echo "⚠ Vault doesn't have gitlab-oauth credentials yet. Creating placeholders..."
PLACEHOLDER_SECRET=$(openssl rand -hex 32)
kubectl exec -n vault "$VAULT_POD" -- env VAULT_TOKEN="$VAULT_TOKEN" vault kv put secret/gitlab-oauth \
client-id="placeholder-gitlab-client-id" \
client-secret="$PLACEHOLDER_SECRET"
echo "✓ Placeholder OAuth credentials created in Vault"
echo " Update with real credentials: ./scripts/sync-gitlab-oauth.sh"
else
echo "✓ GitLab OAuth credentials exist in Vault"
fi
# Wait for external secret to sync the placeholder/real credentials
echo "Waiting for GitLab OIDC External Secret to sync..."
for i in {1..30}; do
STATUS=$(kubectl -n gitlab get externalsecret gitlab-oidc -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$STATUS" = "True" ]; then
echo "✓ GitLab OIDC External Secret synced successfully"
break
fi
sleep 2
done
# Check if SAML secret can sync from Vault
echo "Checking if GitLab SAML External Secret can sync from Vault..."
SAML_SYNCED=false
for i in {1..10}; do
STATUS=$(kubectl -n gitlab get externalsecret gitlab-saml -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$STATUS" = "True" ]; then
echo "✓ GitLab SAML External Secret synced from Vault!"
SAML_SYNCED=true
break
fi
sleep 2
done
# Create placeholder SAML credentials in Vault if they don't exist
echo "Checking if SAML credentials exist in Vault..."
if ! kubectl exec -n vault "$VAULT_POD" -- env VAULT_TOKEN="$VAULT_TOKEN" vault kv get secret/gitlab/saml >/dev/null 2>&1; then
echo "⚠ Vault doesn't have gitlab/saml credentials yet. Creating placeholders..."
kubectl exec -n vault "$VAULT_POD" -- env VAULT_TOKEN="$VAULT_TOKEN" vault kv put secret/gitlab/saml \
idp_sso_url="https://auth.kube.huskypup.net/application/saml/gitlab/sso/binding/redirect/" \
idp_fingerprint="00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"
echo "✓ Placeholder SAML credentials created in Vault"
echo " Update with real credentials: ./scripts/gitlab-saml-bootstrap.sh"
else
echo "✓ GitLab SAML credentials exist in Vault"
fi
# Wait for external secret to sync
echo "Waiting for GitLab SAML External Secret to sync..."
for i in {1..30}; do
STATUS=$(kubectl -n gitlab get externalsecret gitlab-saml -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$STATUS" = "True" ]; then
echo "✓ GitLab SAML External Secret synced successfully"
break
fi
sleep 2
done
# Force password synchronization to prevent authentication issues
echo "=== Synchronizing Database and Redis Passwords ==="
# Delete and recreate database secrets to ensure sync
echo "Refreshing database secrets..."
kubectl delete secret pg-gitlab-app -n gitlab --ignore-not-found=true
kubectl delete secret pg-praefect-app -n gitlab --ignore-not-found=true
# Wait for ExternalSecrets to recreate secrets
echo "Waiting for database secrets to be recreated..."
for i in {1..30}; do
if kubectl -n gitlab get secret pg-gitlab-app >/dev/null 2>&1 && \
kubectl -n gitlab get secret pg-praefect-app >/dev/null 2>&1; then
echo "Database secrets recreated successfully!"
break
fi
echo " waiting for database secrets... (attempt $i/30)"
sleep 2
done
# Delete and recreate Redis secret to ensure sync
echo "Refreshing Redis secrets..."
kubectl delete secret redis-gitlab-secret -n gitlab --ignore-not-found=true
# Wait for Redis ExternalSecret to recreate secret
echo "Waiting for Redis secret to be recreated..."
for i in {1..30}; do
if kubectl -n gitlab get secret redis-gitlab-secret >/dev/null 2>&1; then
echo "Redis secret recreated successfully!"
break
fi
echo " waiting for Redis secret... (attempt $i/30)"
sleep 2
done
# Trigger PostgreSQL reload to pick up new passwords
echo "Triggering PostgreSQL cluster reload..."
kubectl annotate cluster pg-gitlab -n gitlab cnpg.io/reload=$(date +%s) --overwrite
kubectl annotate cluster pg-praefect -n gitlab cnpg.io/reload=$(date +%s) --overwrite
echo ""
echo "===================================================================="
echo "GitLab Bootstrap Summary - Fully Automated"
echo "===================================================================="
echo "✓ Authentik GitLab OAuth blueprint applied"
echo "✓ PostgreSQL CNPG clusters (GitLab + Praefect) deployed"
echo "✓ PgBouncer connection poolers configured with authentication"
echo "✓ Redis standalone instance deployed"
echo "✓ Redis auto-restart automation (CronJob) deployed"
echo "✓ Redis Sentinel HA cluster deployed"
if [ "${OAUTH_SYNCED:-false}" = "true" ]; then
echo "✓ GitLab OIDC credentials synced from Authentik"
else
echo "⚠ GitLab OIDC using placeholder (Authentik provider not ready)"
fi
echo "✓ GitLab SAML secret configured (placeholder)"
echo "✓ Database and Redis passwords synchronized"
echo ""
echo "Password Rotation: Automated via CronJob (hourly checks)"
echo "Next password rotation: $(kubectl get externalsecret -n gitlab gitlab-redis-password -o jsonpath='{.status.refreshTime}' 2>/dev/null || echo 'Unknown') + 24h"
echo "===================================================================="
echo ""
echo "🎉 GitLab bootstrap completed - NO MANUAL STEPS REQUIRED!"
echo ""
echo "GitLab will be accessible at: https://gitlab.kube.huskypup.net"
echo ""
if [ "${OAUTH_SYNCED:-false}" = "false" ]; then
echo "To enable Authentik SSO (optional):"
echo " 1. Wait for Authentik to process the GitLab blueprint (~5 min)"
echo " 2. Run: ./scripts/sync-gitlab-oauth.sh"
echo " 3. Restart GitLab pods to pick up real credentials"
echo ""
fi
echo "For SAML setup (optional): ./scripts/gitlab-saml-bootstrap.sh"
echo ""
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
# Grant GitLab admin access to users in Authentik "authentik Admins" group
# Run this after users login via Authentik SSO
set -euo pipefail
ADMIN_GROUP="authentik Admins"
echo "🔄 Granting GitLab admin access to Authentik admin group members..."
echo ""
# Get Authentik database password
PGPASSWORD=$(kubectl get secret -n authentik pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query Authentik for users in admin group
echo "📋 Getting users from Authentik '$ADMIN_GROUP' group..."
ADMIN_EMAILS=$(kubectl exec -n authentik pg-authentik-1 -- env PGPASSWORD="$PGPASSWORD" psql -h pg-authentik-rw -U app -d app -t -c "
SELECT DISTINCT u.email
FROM authentik_core_user u
JOIN authentik_core_user_groups ug ON u.id = ug.user_id
JOIN authentik_core_group g ON ug.group_id = g.group_uuid
WHERE g.name = '$ADMIN_GROUP' AND u.is_active = true;
" 2>&1 | grep -v "Defaulted" | grep '@' | xargs)
if [ -z "$ADMIN_EMAILS" ]; then
echo "⚠️ No users found in Authentik '$ADMIN_GROUP' group"
exit 0
fi
echo "✓ Found admin users: $ADMIN_EMAILS"
echo ""
# Check if gitlab-toolbox pod exists
TOOLBOX_POD=$(kubectl get pods -n gitlab -l app=toolbox,release=gitlab -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -z "$TOOLBOX_POD" ]; then
echo "❌ GitLab toolbox pod not found"
echo " Toolbox is required to run Rails commands"
exit 1
fi
echo "🔧 Using toolbox pod: $TOOLBOX_POD"
echo ""
# For each admin user, grant admin access
for email in $ADMIN_EMAILS; do
echo "🔐 Processing: $email"
kubectl exec -n gitlab "$TOOLBOX_POD" -- gitlab-rails runner "
user = User.find_by(email: '$email')
if user
if user.admin?
puts ' ✓ Already admin'
else
user.update!(admin: true)
puts ' ✅ Promoted to admin'
end
else
puts ' ⚠️ User not found (needs to login via Authentik SSO first)'
end
" 2>&1 | grep -v "^$"
done
echo ""
echo "✅ Admin sync complete!"
echo ""
echo "💡 Note: Users must login via Authentik SSO at least once before they can be promoted"
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# scripts/grafana-bootstrap.sh
# Grafana presync bootstrap script
set -euo pipefail
echo "=== Grafana Bootstrap ==="
# Ensure namespace exists
kubectl get ns grafana >/dev/null 2>&1 || kubectl create ns grafana
# Apply External Secrets for Grafana OAuth
echo "Applying Grafana OAuth External Secret..."
kubectl apply -f ../base/external-secrets.yaml || echo "Warning: Some external secrets failed to apply (expected if namespaces don't exist)"
# Wait for External Secret to sync
echo "Waiting for Grafana OAuth External Secret to sync..."
for i in {1..30}; do
STATUS=$(kubectl -n grafana get externalsecret grafana-oauth -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$STATUS" = "True" ]; then
echo "Grafana OAuth External Secret is ready!"
break
fi
echo " waiting for External Secret to be ready... (attempt $i/30)"
sleep 2
done
# Verify secret was created
if ! kubectl -n grafana get secret grafana-authentik-oauth >/dev/null 2>&1; then
echo "WARNING: grafana-authentik-oauth secret not found. Ensure Vault has grafana-oauth credentials stored."
echo "Run: ../../scripts/sync-grafana-oauth.sh"
else
echo "Grafana OAuth secret successfully synced!"
fi
# Apply dashboard ConfigMaps
echo "Applying Grafana dashboard ConfigMaps..."
kubectl apply -f grafana/dashboards/
echo "✅ Grafana bootstrap complete"
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# Health check script for Kubernetes cluster
echo "======================================"
echo " Kubernetes Cluster Health Check"
echo "======================================"
echo ""
echo "=== Node Resources ==="
kubectl top nodes 2>&1 || echo "❌ Metrics server not working!"
echo ""
echo "=== High Memory Nodes (>85%) ==="
kubectl top nodes --no-headers | awk '$5 > 85 {print "⚠️ "$1" - "$5"% memory"}'
echo ""
echo "=== Pods without Resource Limits ==="
COUNT=$(kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].resources.limits == null) | .metadata.namespace + "/" + .metadata.name' 2>/dev/null | wc -l)
echo "$COUNT pods without resource limits"
echo ""
echo "=== Failing Pods ==="
kubectl get pods -A --field-selector=status.phase!=Running,status.phase!=Succeeded 2>/dev/null | grep -v "No resources found" || echo "✅ All pods running"
echo ""
echo "=== HPA Status ==="
kubectl get hpa -A 2>/dev/null | grep -v "No resources found" || echo "️ No HPAs configured"
echo ""
echo "=== Pods with High Restarts (>10) ==="
kubectl get pods -A -o json | jq -r '.items[] | select(.status.containerStatuses != null) | select(.status.containerStatuses[].restartCount > 10) | .metadata.namespace + "/" + .metadata.name + " - " + (.status.containerStatuses[].restartCount|tostring) + " restarts"' 2>/dev/null || echo "✅ No pods with excessive restarts"
echo ""
echo "=== Storage Status ==="
kubectl get pv | grep -c "Bound"
echo "persistent volumes bound"
echo ""
echo "=== Ingress Status ==="
kubectl get ingress -A | tail -n +2 | wc -l
echo "ingresses configured"
echo ""
echo "======================================"
echo " Health Check Complete"
echo "======================================"
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
# Home Assistant Bootstrap - Auto-configure OIDC with hass-openid integration
set -euo pipefail
echo "=== Home Assistant Bootstrap - Fully Automated ==="
cd "$(dirname "$0")/.." || exit 1
NS=home-assistant
# 1) Ensure namespace
kubectl get ns "${NS}" >/dev/null 2>&1 || kubectl create ns "${NS}"
# 2) Apply ExternalSecret for OIDC credentials
echo "Applying Home Assistant OIDC ExternalSecret..."
kubectl apply -f apps/home-assistant/external-secret.yaml
# 3) Wait for ESO to sync OIDC credentials
echo "Waiting for OIDC credentials to sync from Vault..."
for i in {1..30}; do
if kubectl -n "${NS}" get secret homeassistant-oidc-secret >/dev/null 2>&1; then
echo "OIDC credentials synced successfully"
break
fi
echo " waiting... (attempt $i/30)"
sleep 2
done
# 4) Wait for the PVC to be bound
echo "Waiting for PVC to be bound..."
for i in {1..60}; do
if kubectl -n "${NS}" get pvc home-assistant-config >/dev/null 2>&1; then
PVC_STATUS=$(kubectl -n "${NS}" get pvc home-assistant-config -o jsonpath='{.status.phase}')
if [ "${PVC_STATUS}" = "Bound" ]; then
echo "PVC is bound"
break
fi
fi
echo " waiting for PVC... (attempt $i/60)"
sleep 2
done
# 5) Get the running Home Assistant pod (if any)
POD_NAME=$(kubectl get pods -n "${NS}" -l app.kubernetes.io/name=home-assistant -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "${POD_NAME}" ] && kubectl -n "${NS}" get pod "${POD_NAME}" >/dev/null 2>&1; then
echo "Configuring existing Home Assistant pod: ${POD_NAME}"
# Get OIDC credentials
CLIENT_ID=$(kubectl get secret -n "${NS}" homeassistant-oidc-secret -o jsonpath='{.data.client_id}' | base64 -d)
CLIENT_SECRET=$(kubectl get secret -n "${NS}" homeassistant-oidc-secret -o jsonpath='{.data.client_secret}' | base64 -d)
# Create secrets.yaml
echo "Creating secrets.yaml..."
kubectl exec -n "${NS}" "${POD_NAME}" -- sh -c "cat > /config/secrets.yaml <<EOF
oidc_client_id: \"${CLIENT_ID}\"
oidc_client_secret: \"${CLIENT_SECRET}\"
EOF"
# Create oidc.yaml using Python to avoid YAML tag issues
echo "Creating oidc.yaml..."
kubectl exec -n "${NS}" "${POD_NAME}" -- python3 -c "
import os
oidc_content = '''openid:
client_id: \"!secret oidc_client_id\"
client_secret: \"!secret oidc_client_secret\"
configure_url: \"https://auth.kube.huskypup.net/application/o/home-assistant/.well-known/openid-configuration\"
scope: \"openid profile email\"
username_field: \"preferred_username\"
block_login: false'''
with open('/config/oidc.yaml', 'w') as f:
f.write(oidc_content)
"
# Update configuration.yaml if needed
echo "Updating configuration.yaml..."
kubectl exec -n "${NS}" "${POD_NAME}" -- sh -c "
if ! grep -q 'packages:' /config/configuration.yaml 2>/dev/null; then
echo '' >> /config/configuration.yaml
echo 'homeassistant:' >> /config/configuration.yaml
echo ' packages: !include_dir_merge_named oidc' >> /config/configuration.yaml
fi
if ! grep -q 'use_x_forwarded_for:' /config/configuration.yaml 2>/dev/null; then
cat >> /config/configuration.yaml <<'EOF'
http:
use_x_forwarded_for: true
trusted_proxies:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 127.0.0.1
- ::1
EOF
fi
"
echo "Configuration complete. Restarting Home Assistant..."
kubectl rollout restart deployment -n "${NS}" home-assistant
kubectl rollout status deployment -n "${NS}" home-assistant --timeout=300s
else
echo "No running Home Assistant pod found. Configuration will be applied when pod starts."
echo "The initContainer will install hass-openid, and the configuration will be set up on first boot."
fi
echo "Home Assistant bootstrap complete!"
+69
View File
@@ -0,0 +1,69 @@
#!/bin/bash
set -euo pipefail
# Import Grafana dashboards from grafana.com
# Usage: ./import-grafana-dashboards.sh
GRAFANA_URL="http://localhost:3000"
GRAFANA_USER="admin"
GRAFANA_PASSWORD=$(kubectl get secret -n grafana grafana -o jsonpath="{.data.admin-password}" | base64 --decode)
# Dashboard IDs and their folders
declare -A DASHBOARDS
DASHBOARDS[7249]="Kubernetes" # K8s Cluster
DASHBOARDS[11663]="Kubernetes" # K8s Resources Cluster
DASHBOARDS[11664]="Kubernetes" # K8s Resources Namespace
DASHBOARDS[11665]="Kubernetes" # K8s Resources Pod
DASHBOARDS[11001]="Infrastructure" # Cert-Manager
DASHBOARDS[16888]="Infrastructure" # Longhorn
DASHBOARDS[9628]="Infrastructure" # PostgreSQL
DASHBOARDS[14584]="Infrastructure" # ArgoCD
echo "Starting port-forward to Grafana..."
kubectl port-forward -n grafana svc/grafana 3000:80 &
PF_PID=$!
sleep 5
# Create folders
echo "Creating folders..."
for folder in "Kubernetes" "Infrastructure"; do
curl -s -X POST -H "Content-Type: application/json" \
-u "${GRAFANA_USER}:${GRAFANA_PASSWORD}" \
-d "{\"title\":\"${folder}\"}" \
"${GRAFANA_URL}/api/folders" || true
done
# Import dashboards
for dashboard_id in "${!DASHBOARDS[@]}"; do
folder="${DASHBOARDS[$dashboard_id]}"
echo "Importing dashboard ${dashboard_id} to folder ${folder}..."
# Get folder UID
folder_uid=$(curl -s -u "${GRAFANA_USER}:${GRAFANA_PASSWORD}" \
"${GRAFANA_URL}/api/folders" | \
jq -r ".[] | select(.title==\"${folder}\") | .uid")
# Download and import dashboard
dashboard_json=$(curl -s "https://grafana.com/api/dashboards/${dashboard_id}/revisions/1/download")
# Prepare import payload
import_payload=$(jq -n \
--arg folderUid "$folder_uid" \
--argjson dashboard "$dashboard_json" \
'{
dashboard: $dashboard,
folderUid: $folderUid,
overwrite: true
}')
curl -s -X POST -H "Content-Type: application/json" \
-u "${GRAFANA_USER}:${GRAFANA_PASSWORD}" \
-d "$import_payload" \
"${GRAFANA_URL}/api/dashboards/db" | jq -r '.status'
done
echo "Stopping port-forward..."
kill $PF_PID
echo "Dashboard import complete!"
echo "Access Grafana at: https://grafana.kube.huskypup.net"
+104
View File
@@ -0,0 +1,104 @@
#!/bin/bash
# Import Unifi dashboards into Grafana via API
# This script downloads the dashboards from GitHub and imports them properly
set -euo pipefail
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${GREEN}=== Importing Unifi Dashboards to Grafana ===${NC}"
echo ""
# Port forward to Grafana
echo "Setting up port-forward to Grafana..."
kubectl -n grafana port-forward svc/grafana 3000:80 >/dev/null 2>&1 &
PF_PID=$!
sleep 3
# Grafana API credentials (using default admin/admin for now)
GRAFANA_URL="http://localhost:3000"
API_KEY=""
# Function to cleanup
cleanup() {
echo ""
echo "Cleaning up port-forward..."
kill $PF_PID 2>/dev/null || true
}
trap cleanup EXIT
# Get admin password from Kubernetes secret or use default
ADMIN_PASS=$(kubectl -n grafana get secret grafana -o jsonpath='{.data.admin-password}' 2>/dev/null | base64 -d || echo "admin")
# Dashboards to import
DASHBOARDS=("unifi-access-points" "unifi-clients" "unifi-gateway" "unifi-pdu" "unifi-sites" "unifi-switches")
for dashboard in "${DASHBOARDS[@]}"; do
echo -e "${YELLOW}Importing ${dashboard}...${NC}"
# Download dashboard
curl -sL "https://raw.githubusercontent.com/timothystewart6/unpoller-unifi/main/grafana/provisioning/dashboards/${dashboard}.json" -o "/tmp/${dashboard}.json"
# Fix datasource references and wrap for import
python3 << PYEOF
import json
with open('/tmp/${dashboard}.json', 'r') as f:
data = json.load(f)
# Remove template fields
data.pop('__inputs', None)
data.pop('__requires', None)
data.pop('id', None)
# Fix datasource references
def fix_datasource(obj):
if isinstance(obj, dict):
for key, value in obj.items():
if key == 'datasource':
if isinstance(value, str) and ('DS_PROMETHEUS' in value or value == ''):
obj[key] = {'type': 'prometheus', 'uid': 'Prometheus'}
elif isinstance(value, dict) and value.get('type') == 'prometheus':
obj[key] = {'type': 'prometheus', 'uid': 'Prometheus'}
else:
fix_datasource(value)
elif isinstance(obj, list):
for item in obj:
fix_datasource(item)
fix_datasource(data)
# Wrap for import API
import_data = {
"dashboard": data,
"overwrite": True,
"inputs": [],
"folderId": 0
}
with open('/tmp/${dashboard}-import.json', 'w') as f:
json.dump(import_data, f)
PYEOF
# Import via API
response=$(curl -s -X POST \
-H "Content-Type: application/json" \
-u "admin:${ADMIN_PASS}" \
-d @/tmp/${dashboard}-import.json \
"${GRAFANA_URL}/api/dashboards/db")
if echo "$response" | grep -q '"status":"success"'; then
echo -e "${GREEN}✓ Successfully imported ${dashboard}${NC}"
else
echo -e "${YELLOW}⚠ Failed to import ${dashboard}: $response${NC}"
fi
rm -f "/tmp/${dashboard}.json" "/tmp/${dashboard}-import.json"
done
echo ""
echo -e "${GREEN}✓ Dashboard import complete!${NC}"
echo ""
echo "Visit https://grafana.kube.huskypup.net and search for 'Unifi' to find your dashboards."
+16
View File
@@ -0,0 +1,16 @@
# https://longhorn.io/docs/1.6.2/deploy/important-notes/#pod-security-policies-disabled--pod-security-admission-introduction
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Namespace
metadata:
name: metallb-system
labels:
pod-security.kubernetes.io/enforce: privileged
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: privileged
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: privileged
pod-security.kubernetes.io/warn-version: latest
EOF
+121
View File
@@ -0,0 +1,121 @@
#!/bin/bash
# Nessus Bootstrap Script - Fully Automated
# This script runs as a presync hook to set up Nessus dependencies
set -euo pipefail
echo "=================================================================="
echo "Nessus Presync - Fully Automated Bootstrap"
echo "=================================================================="
echo "This will set up:"
echo " ✓ PostgreSQL database (CloudNativePG) with low resources"
echo " ✓ Auto-generated database password (via ESO)"
echo " ✓ Admin credentials from Vault"
echo " ✓ Persistent storage for scan data"
echo " ✓ OAuth2-Proxy authentication (Authentik SSO)"
echo ""
echo "Expected time: 3-5 minutes"
echo "=================================================================="
echo ""
NS=nessus
CLUSTER=pg-nessus
# Get the script directory and move to repo root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
cd "${REPO_ROOT}/apps" || exit 1
# 1) Ensure namespace exists
echo "⚙️ Creating namespace ${NS}..."
kubectl get ns "${NS}" >/dev/null 2>&1 || kubectl create ns "${NS}"
# 2) Apply CNPG cluster (auto-generates DB password)
echo "⚙️ Deploying PostgreSQL cluster (low resources)..."
kubectl apply -f nessus/cnpg-cluster.yaml
# 3) Wait for CNPG cluster to be ready
echo "⏳ Waiting for PostgreSQL cluster to be ready..."
kubectl -n "${NS}" wait --for=condition=Ready "cluster/${CLUSTER}" --timeout=300s || {
echo "⚠️ WARNING: PostgreSQL cluster not ready yet"
echo " This is normal on first deployment - rerun 'helmfile apply' in a few minutes"
exit 0
}
echo "✅ PostgreSQL cluster ready"
echo ""
# 4) Apply ESO secrets (auto-rotate DB passwords)
echo "⚙️ Configuring auto-rotating database passwords..."
kubectl apply -f nessus/cnpg-secrets.yaml
# Wait for DB secret to sync
echo "⏳ Waiting for database secret to sync..."
for i in {1..30}; do
if kubectl -n "${NS}" get secret nessus-db-secret >/dev/null 2>&1; then
echo "✅ Database secret synced"
break
fi
echo " waiting... (attempt $i/30)"
sleep 2
done
# 5) Apply Nessus admin credentials ExternalSecret
echo "⚙️ Syncing admin credentials from Vault..."
kubectl apply -f nessus/external-secret.yaml
# Wait for admin secret to sync
echo "⏳ Waiting for admin credentials to sync from Vault..."
for i in {1..30}; do
if kubectl -n "${NS}" get secret nessus-admin-credentials >/dev/null 2>&1; then
echo "✅ Admin credentials synced from Vault"
break
fi
echo " waiting... (attempt $i/30)"
sleep 2
done
# Check if credentials exist in Vault
if ! kubectl -n "${NS}" get secret nessus-admin-credentials >/dev/null 2>&1; then
echo ""
echo "⚠️ WARNING: Nessus admin credentials not found in Vault!"
echo ""
echo "BOOTSTRAP REQUIRED - Run this command first:"
echo "=================================================================="
echo "kubectl exec -n vault vault-0 -- vault kv put secret/nessus \\"
echo " admin-username=\"admin\" \\"
echo " admin-password=\"YourSecurePassword123!\""
echo "=================================================================="
echo ""
echo "After storing credentials, rerun: helmfile apply"
echo ""
exit 1
fi
# 6) Apply PVC for Nessus scan data
echo "⚙️ Creating persistent storage for scan data..."
kubectl apply -f nessus/pvc.yaml
# 7) Apply Nessus deployment
echo "⚙️ Deploying Nessus scanner..."
kubectl apply -f nessus/deployment.yaml
# 8) Apply Nessus ingress
echo "⚙️ Configuring ingress with OAuth2-Proxy..."
kubectl apply -f nessus/ingress.yaml
echo ""
echo "✅ Nessus presync complete - fully automated!"
echo ""
echo "=================================================================="
echo "IMPORTANT: First-time setup instructions"
echo "=================================================================="
echo ""
echo "If you haven't already, store Nessus credentials in Vault:"
echo ""
echo "kubectl exec -n vault vault-0 -- vault kv put secret/nessus \\"
echo " admin-username=\"admin\" \\"
echo " admin-password=\"YourSecurePassword123!\""
echo ""
echo "Then rerun: helmfile apply"
echo "=================================================================="
echo ""
+203
View File
@@ -0,0 +1,203 @@
---
# iperf3 server on node-41
apiVersion: v1
kind: Pod
metadata:
name: iperf3-server
namespace: default
labels:
app: iperf3-server
spec:
nodeSelector:
kubernetes.io/hostname: talos-node-41
containers:
- name: iperf3
image: networkstatic/iperf3:latest
command: ["iperf3"]
args: ["-s"]
ports:
- containerPort: 5201
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
---
# iperf3 server service
apiVersion: v1
kind: Service
metadata:
name: iperf3-server
namespace: default
spec:
selector:
app: iperf3-server
ports:
- protocol: TCP
port: 5201
targetPort: 5201
type: ClusterIP
---
# iperf3 client on node-42
apiVersion: v1
kind: Pod
metadata:
name: iperf3-client-node42
namespace: default
labels:
app: iperf3-client
test: node42
spec:
nodeSelector:
kubernetes.io/hostname: talos-node-42
restartPolicy: Never
containers:
- name: iperf3
image: networkstatic/iperf3:latest
command: ["/bin/sh"]
args:
- -c
- |
echo "Waiting for iperf3 server to be ready..."
sleep 5
echo ""
echo "=========================================="
echo "Network Speed Test: node-42 → node-41"
echo "=========================================="
echo ""
echo "Test 1: TCP Bandwidth (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4
echo ""
echo "=========================================="
echo ""
echo "Test 2: TCP Bandwidth with larger window (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4 -w 256K
echo ""
echo "=========================================="
echo "Test Complete!"
echo "=========================================="
sleep 60
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
---
# iperf3 client on node-43
apiVersion: v1
kind: Pod
metadata:
name: iperf3-client-node43
namespace: default
labels:
app: iperf3-client
test: node43
spec:
nodeSelector:
kubernetes.io/hostname: talos-node-43
restartPolicy: Never
containers:
- name: iperf3
image: networkstatic/iperf3:latest
command: ["/bin/sh"]
args:
- -c
- |
echo "Waiting for iperf3 server to be ready..."
sleep 5
echo ""
echo "=========================================="
echo "Network Speed Test: node-43 → node-41"
echo "=========================================="
echo ""
echo "Test 1: TCP Bandwidth (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4
echo ""
echo "=========================================="
echo ""
echo "Test 2: TCP Bandwidth with larger window (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4 -w 256K
echo ""
echo "=========================================="
echo "Test Complete!"
echo "=========================================="
sleep 60
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
---
# iperf3 client on node-44
apiVersion: v1
kind: Pod
metadata:
name: iperf3-client-node44
namespace: default
labels:
app: iperf3-client
test: node44
spec:
nodeSelector:
kubernetes.io/hostname: talos-node-44
restartPolicy: Never
containers:
- name: iperf3
image: networkstatic/iperf3:latest
command: ["/bin/sh"]
args:
- -c
- |
echo "Waiting for iperf3 server to be ready..."
sleep 5
echo ""
echo "=========================================="
echo "Network Speed Test: node-44 → node-41"
echo "=========================================="
echo ""
echo "Test 1: TCP Bandwidth (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4
echo ""
echo "=========================================="
echo ""
echo "Test 2: TCP Bandwidth with larger window (10 seconds)"
iperf3 -c iperf3-server -t 10 -P 4 -w 256K
echo ""
echo "=========================================="
echo "Test Complete!"
echo "=========================================="
sleep 60
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
+231
View File
@@ -0,0 +1,231 @@
#!/bin/bash
# Rook-Ceph Preparation Script
# Wipes NVMe drives on all nodes, creates namespace with proper PSS labels,
# cleans up stale resources, and verifies node availability
set -euo pipefail
NS="rook-ceph"
WIPE_IMAGE="quay.io/ceph/ceph:v19.2.0"
# Node to NVMe device mapping
declare -A NODE_NVME=(
["talos-cp-01"]="nvme0n1"
["talos-cp-02"]="nvme1n1"
["talos-cp-03"]="nvme0n1"
["talos-cp-04"]="nvme0n1"
)
echo "=== Rook-Ceph Preparation ==="
# Create namespace if it doesn't exist
if ! kubectl get ns "${NS}" >/dev/null 2>&1; then
echo "Creating namespace ${NS}..."
kubectl create ns "${NS}"
fi
# Apply privileged Pod Security Standards (required for Ceph)
echo "Applying privileged Pod Security Standards..."
kubectl label namespace "${NS}" pod-security.kubernetes.io/enforce=privileged --overwrite
kubectl label namespace "${NS}" pod-security.kubernetes.io/audit=privileged --overwrite
kubectl label namespace "${NS}" pod-security.kubernetes.io/warn=privileged --overwrite
# Clean up any stale OSD deployments
echo "Cleaning up stale OSD resources..."
for osd_deploy in $(kubectl -n "${NS}" get deployments -o name 2>/dev/null | grep rook-ceph-osd- || true); do
echo " Deleting ${osd_deploy}..."
kubectl -n "${NS}" delete "${osd_deploy}" --ignore-not-found=true
done
# Clean up stale OSD prepare pods
kubectl -n "${NS}" delete pods -l app=rook-ceph-osd-prepare --ignore-not-found=true 2>/dev/null || true
# Clean up any stale finalizers from previous failed deployments
echo "Checking for stale CephCluster..."
if kubectl -n "${NS}" get cephcluster rook-ceph >/dev/null 2>&1; then
PHASE=$(kubectl -n "${NS}" get cephcluster rook-ceph -o jsonpath='{.status.phase}' 2>/dev/null || echo "Unknown")
if [ "$PHASE" = "Deleting" ]; then
echo "Found stale CephCluster in Deleting state, cleaning up finalizers..."
kubectl -n "${NS}" patch cephcluster rook-ceph --type merge -p '{"metadata":{"finalizers":[]}}' 2>/dev/null || true
fi
fi
# Function to wipe NVMe on a specific node
wipe_nvme() {
local node=$1
local device=$2
local pod_name="wipe-nvme-${node}"
echo "Wiping /dev/${device} on ${node}..."
# Delete any existing wipe pod
kubectl -n "${NS}" delete pod "${pod_name}" --ignore-not-found=true 2>/dev/null || true
# Create wipe pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: ${pod_name}
namespace: ${NS}
spec:
restartPolicy: Never
nodeName: ${node}
tolerations:
- operator: Exists
containers:
- name: wipe
image: ${WIPE_IMAGE}
securityContext:
privileged: true
command:
- /bin/bash
- -c
- |
set -ex
DEVICE="/dev/${device}"
# Skip if device doesn't exist
if [ ! -b "\${DEVICE}" ]; then
echo "Device \${DEVICE} not found, skipping"
exit 0
fi
echo "=== Wiping \${DEVICE} on ${node} ==="
# Remove any Ceph LVM volumes
for vg in \$(vgs --noheadings -o vg_name 2>/dev/null | grep -i ceph || true); do
echo "Removing VG: \${vg}"
vgremove -ff "\${vg}" 2>/dev/null || true
done
# Remove PV if exists
pvremove -ff "\${DEVICE}" 2>/dev/null || true
# Remove device mapper entries
dmsetup remove_all -f 2>/dev/null || true
# Wipe filesystem signatures
wipefs -af "\${DEVICE}"
# Zap GPT/MBR
sgdisk --zap-all "\${DEVICE}"
# Zero first 100MB (clears any remaining metadata)
dd if=/dev/zero of="\${DEVICE}" bs=1M count=100 conv=fsync
# Zero last 100MB (clears backup GPT)
SECTORS=\$(blockdev --getsz "\${DEVICE}")
dd if=/dev/zero of="\${DEVICE}" bs=1M count=100 seek=\$((SECTORS/2048 - 100)) conv=fsync
echo "=== Wipe complete for \${DEVICE} ==="
lsblk -f "\${DEVICE}"
blkid "\${DEVICE}" || echo "No signatures (clean)"
volumeMounts:
- name: dev
mountPath: /dev
volumes:
- name: dev
hostPath:
path: /dev
EOF
# Wait for pod to complete
echo " Waiting for wipe to complete on ${node}..."
if ! kubectl -n "${NS}" wait --for=condition=Ready pod/"${pod_name}" --timeout=30s 2>/dev/null; then
# Pod might have completed already
true
fi
# Wait for completion (up to 2 minutes)
local timeout=120
local elapsed=0
while [ $elapsed -lt $timeout ]; do
local phase=$(kubectl -n "${NS}" get pod "${pod_name}" -o jsonpath='{.status.phase}' 2>/dev/null || echo "Unknown")
if [ "$phase" = "Succeeded" ]; then
echo " ✓ Wipe completed on ${node}"
kubectl -n "${NS}" logs "${pod_name}" 2>/dev/null | tail -5
kubectl -n "${NS}" delete pod "${pod_name}" --ignore-not-found=true
return 0
elif [ "$phase" = "Failed" ]; then
echo " ✗ Wipe failed on ${node}"
kubectl -n "${NS}" logs "${pod_name}" 2>/dev/null | tail -20
kubectl -n "${NS}" delete pod "${pod_name}" --ignore-not-found=true
return 1
fi
sleep 5
elapsed=$((elapsed + 5))
done
echo " ✗ Wipe timed out on ${node}"
kubectl -n "${NS}" delete pod "${pod_name}" --ignore-not-found=true
return 1
}
# Verify nodes are available
echo "Verifying node availability..."
NODE_COUNT=$(kubectl get nodes --no-headers 2>/dev/null | wc -l)
if [ "${NODE_COUNT}" -lt 1 ]; then
echo "ERROR: No nodes available in cluster"
exit 1
fi
echo "Found ${NODE_COUNT} node(s) available"
# Check for Ready nodes
READY_NODES=$(kubectl get nodes --no-headers 2>/dev/null | grep -c " Ready" || echo "0")
if [ "${READY_NODES}" -lt 1 ]; then
echo "ERROR: No Ready nodes available in cluster"
exit 1
fi
echo "Found ${READY_NODES} Ready node(s)"
# Check if Ceph OSDs already exist (skip wipe if so, unless forced)
FORCE_WIPE="${FORCE_WIPE:-false}"
if [ "$FORCE_WIPE" != "true" ]; then
OSD_COUNT=$(kubectl -n "${NS}" get pods -l app=rook-ceph-osd --no-headers 2>/dev/null | grep -c Running || echo "0")
if [ "$OSD_COUNT" -gt 0 ]; then
echo ""
echo "WARNING: Found ${OSD_COUNT} running OSD(s). Skipping disk wipe to protect existing data."
echo "To force wipe, run: FORCE_WIPE=true ./scripts/rook-ceph-prepare.sh"
echo ""
echo "=== Rook-Ceph preparation complete (no wipe) ==="
exit 0
fi
fi
# Wipe NVMe drives on all configured nodes
echo ""
echo "=== Wiping NVMe drives ==="
WIPE_FAILED=0
for node in "${!NODE_NVME[@]}"; do
device="${NODE_NVME[$node]}"
# Check if node exists in cluster
if ! kubectl get node "${node}" >/dev/null 2>&1; then
echo "Node ${node} not found in cluster, skipping..."
continue
fi
if ! wipe_nvme "${node}" "${device}"; then
echo "WARNING: Failed to wipe ${device} on ${node}"
WIPE_FAILED=1
fi
done
if [ $WIPE_FAILED -eq 1 ]; then
echo ""
echo "WARNING: Some wipe operations failed. Check logs above."
echo "Continuing with Ceph setup anyway..."
fi
# List nodes for informational purposes
echo ""
echo "Cluster nodes:"
kubectl get nodes -o wide --no-headers 2>/dev/null | while read -r line; do
echo " $line"
done
echo ""
echo "=== Rook-Ceph preparation complete ==="
echo "NVMe drives have been wiped and namespace is ready."
echo "Run 'helmfile apply' to deploy Ceph."
+184
View File
@@ -0,0 +1,184 @@
#!/bin/bash
set -e
echo "Starting Network Benchmark Between Nodes..."
echo ""
# Deploy iperf3 server on node-41
echo "Deploying iperf3 server on node-41..."
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: iperf3-server
namespace: default
labels:
app: iperf3-server
spec:
nodeSelector:
kubernetes.io/hostname: talos-node-41
containers:
- name: iperf3
image: networkstatic/iperf3:latest
command: ["iperf3"]
args: ["-s"]
ports:
- containerPort: 5201
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
runAsNonRoot: true
runAsUser: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
---
apiVersion: v1
kind: Service
metadata:
name: iperf3-server
namespace: default
spec:
selector:
app: iperf3-server
ports:
- protocol: TCP
port: 5201
targetPort: 5201
type: ClusterIP
EOF
echo "Waiting for server to be ready..."
kubectl wait --for=condition=ready pod/iperf3-server -n default --timeout=60s
sleep 2
echo ""
echo "=========================================="
echo "Test 1: node-42 → node-41"
echo "=========================================="
kubectl run iperf3-client-42 \
--image=networkstatic/iperf3:latest \
--restart=Never \
--rm -i \
--overrides='
{
"spec": {
"nodeSelector": {
"kubernetes.io/hostname": "talos-node-42"
},
"securityContext": {
"runAsNonRoot": true,
"runAsUser": 1000,
"seccompProfile": {
"type": "RuntimeDefault"
}
},
"containers": [{
"name": "iperf3-client-42",
"image": "networkstatic/iperf3:latest",
"stdin": true,
"tty": true,
"command": ["iperf3", "-c", "iperf3-server", "-t", "10", "-P", "4"],
"securityContext": {
"allowPrivilegeEscalation": false,
"capabilities": {
"drop": ["ALL"]
},
"runAsNonRoot": true,
"runAsUser": 1000
}
}]
}
}' 2>/dev/null || true
echo ""
echo "=========================================="
echo "Test 2: node-43 → node-41"
echo "=========================================="
kubectl run iperf3-client-43 \
--image=networkstatic/iperf3:latest \
--restart=Never \
--rm -i \
--overrides='
{
"spec": {
"nodeSelector": {
"kubernetes.io/hostname": "talos-node-43"
},
"securityContext": {
"runAsNonRoot": true,
"runAsUser": 1000,
"seccompProfile": {
"type": "RuntimeDefault"
}
},
"containers": [{
"name": "iperf3-client-43",
"image": "networkstatic/iperf3:latest",
"stdin": true,
"tty": true,
"command": ["iperf3", "-c", "iperf3-server", "-t", "10", "-P", "4"],
"securityContext": {
"allowPrivilegeEscalation": false,
"capabilities": {
"drop": ["ALL"]
},
"runAsNonRoot": true,
"runAsUser": 1000
}
}]
}
}' 2>/dev/null || true
echo ""
echo "=========================================="
echo "Test 3: node-44 → node-41"
echo "=========================================="
kubectl run iperf3-client-44 \
--image=networkstatic/iperf3:latest \
--restart=Never \
--rm -i \
--overrides='
{
"spec": {
"nodeSelector": {
"kubernetes.io/hostname": "talos-node-44"
},
"securityContext": {
"runAsNonRoot": true,
"runAsUser": 1000,
"seccompProfile": {
"type": "RuntimeDefault"
}
},
"containers": [{
"name": "iperf3-client-44",
"image": "networkstatic/iperf3:latest",
"stdin": true,
"tty": true,
"command": ["iperf3", "-c", "iperf3-server", "-t", "10", "-P", "4"],
"securityContext": {
"allowPrivilegeEscalation": false,
"capabilities": {
"drop": ["ALL"]
},
"runAsNonRoot": true,
"runAsUser": 1000
}
}]
}
}' 2>/dev/null || true
echo ""
echo "=========================================="
echo "Cleaning up..."
kubectl delete pod iperf3-server -n default
kubectl delete service iperf3-server -n default
echo ""
echo "Network benchmark complete!"
+126
View File
@@ -0,0 +1,126 @@
#!/bin/bash
set -euo pipefail
# setup-ceph-saml.sh
# Configures Ceph Dashboard SAML2 SSO with Authentik
# This script should be run after both Ceph and Authentik are deployed
CEPH_NAMESPACE="rook-ceph"
AUTHENTIK_NAMESPACE="authentik"
CEPH_BASE_URL="https://ceph.kube.huskypup.net"
AUTHENTIK_SAML_METADATA="https://auth.kube.huskypup.net/application/saml/ceph-dashboard/metadata/"
USERNAME_ATTRIBUTE="username"
echo "=== Setting up Ceph Dashboard SAML2 SSO ==="
# Check if Ceph tools pod is available
echo "Checking for Ceph tools pod..."
if ! kubectl -n "${CEPH_NAMESPACE}" get deploy rook-ceph-tools >/dev/null 2>&1; then
echo "ERROR: rook-ceph-tools deployment not found"
exit 1
fi
# Wait for Ceph tools to be ready
echo "Waiting for Ceph tools pod to be ready..."
kubectl -n "${CEPH_NAMESPACE}" wait --for=condition=Available deployment/rook-ceph-tools --timeout=120s || {
echo "WARNING: Ceph tools pod may not be ready. Continuing anyway..."
}
# Check Ceph cluster health
echo "Checking Ceph cluster health..."
CEPH_HEALTH=$(kubectl -n "${CEPH_NAMESPACE}" exec deploy/rook-ceph-tools -- ceph health 2>/dev/null || echo "UNKNOWN")
echo "Ceph health: ${CEPH_HEALTH}"
if [[ "$CEPH_HEALTH" == "UNKNOWN" ]]; then
echo "ERROR: Cannot communicate with Ceph cluster"
exit 1
fi
# Check if Authentik SAML metadata is accessible
echo "Verifying Authentik SAML metadata endpoint..."
HTTP_CODE=$(curl -sk -o /dev/null -w "%{http_code}" "${AUTHENTIK_SAML_METADATA}" 2>/dev/null || echo "000")
if [[ "$HTTP_CODE" != "200" && "$HTTP_CODE" != "302" ]]; then
echo "WARNING: Authentik SAML metadata endpoint returned HTTP ${HTTP_CODE}"
echo "The Ceph SAML application may not be configured in Authentik yet."
echo "Ensure the ceph-blueprint.yaml is applied to Authentik."
fi
# Configure SAML2 SSO
echo "Configuring SAML2 SSO..."
SAML_CONFIG=$(kubectl -n "${CEPH_NAMESPACE}" exec deploy/rook-ceph-tools -- \
ceph dashboard sso setup saml2 \
"${CEPH_BASE_URL}" \
"${AUTHENTIK_SAML_METADATA}" \
"${USERNAME_ATTRIBUTE}" 2>&1) || {
echo "ERROR: Failed to configure SAML2 SSO"
echo "$SAML_CONFIG"
exit 1
}
echo "SAML2 configuration applied successfully"
# Enable SSO
echo "Enabling SAML2 SSO..."
kubectl -n "${CEPH_NAMESPACE}" exec deploy/rook-ceph-tools -- \
ceph dashboard sso enable saml2
# Verify SSO status
SSO_STATUS=$(kubectl -n "${CEPH_NAMESPACE}" exec deploy/rook-ceph-tools -- \
ceph dashboard sso status 2>/dev/null)
echo "SSO Status: ${SSO_STATUS}"
# Create SSO users from Authentik
echo ""
echo "Creating SSO users in Ceph Dashboard..."
# Get active users from Authentik database
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "$CNPG_POD" ]; then
# Get password from secret
DB_PASSWORD=$(kubectl -n "${AUTHENTIK_NAMESPACE}" get secret pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query for active non-service users
USERS=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -c postgres -- \
sh -c "PGPASSWORD='${DB_PASSWORD}' psql -h localhost -U app -d app -t -c \"SELECT username FROM authentik_core_user WHERE is_active = true AND username NOT LIKE 'ak-%' AND username != 'AnonymousUser';\"" 2>/dev/null | tr -d ' ' | grep -v '^$' || echo "")
if [ -n "$USERS" ]; then
echo "Found users in Authentik: $(echo $USERS | tr '\n' ' ')"
# Get existing Ceph users
EXISTING_USERS=$(kubectl -n "${CEPH_NAMESPACE}" exec deploy/rook-ceph-tools -- \
ceph dashboard ac-user-show 2>/dev/null | tr -d '[]"' | tr ',' '\n' | tr -d ' ')
for USER in $USERS; do
if echo "$EXISTING_USERS" | grep -q "^${USER}$"; then
echo " User '${USER}' already exists in Ceph"
else
echo " Creating user '${USER}' with administrator role..."
# Create user with a temporary password (SSO will bypass password auth)
echo "sso-managed-password-$(date +%s)" | kubectl -n "${CEPH_NAMESPACE}" exec -i deploy/rook-ceph-tools -- \
ceph dashboard ac-user-create "${USER}" -i - administrator 2>/dev/null && \
echo " Created user '${USER}'" || \
echo " WARNING: Failed to create user '${USER}'"
fi
done
else
echo "No users found in Authentik to sync"
fi
else
echo "WARNING: Could not find Authentik PostgreSQL pod. Skipping user sync."
echo "You may need to manually create Ceph users matching your Authentik usernames."
fi
echo ""
echo "=== Ceph Dashboard SAML2 SSO Setup Complete ==="
echo ""
echo "Configuration:"
echo " Base URL: ${CEPH_BASE_URL}"
echo " IdP Metadata: ${AUTHENTIK_SAML_METADATA}"
echo " Username Attribute: ${USERNAME_ATTRIBUTE}"
echo ""
echo "To login:"
echo " 1. Navigate to ${CEPH_BASE_URL}/auth/saml2/login"
echo " 2. Authenticate with Authentik"
echo " 3. You will be redirected to Ceph Dashboard"
echo ""
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# setup-netbird-api-token.sh - Bootstrap Netbird API token for operator + exporter
#
# Creates a service user in Netbird and generates a long-lived PAT (Personal Access Token)
# for automation. Stores the token in Vault for ExternalSecret consumption.
#
# Usage:
# ./scripts/setup-netbird-api-token.sh <admin-pat>
#
# Prerequisites:
# - Netbird management server running and accessible
# - Admin PAT from Netbird dashboard (Settings → Personal Access Tokens)
# - Vault initialized and unsealed
# - kubectl configured for the cluster
set -euo pipefail
ADMIN_PAT="${1:?Usage: $0 <admin-pat>}"
NETBIRD_API="https://netbird.kube.huskypup.net"
SERVICE_USER_NAME="k8s-operator"
PAT_NAME="operator-automation"
echo "=== Netbird API Token Bootstrap ==="
# Check for existing service user
echo "Checking for existing service user '${SERVICE_USER_NAME}'..."
USERS=$(curl -sf -H "Authorization: Token ${ADMIN_PAT}" \
"${NETBIRD_API}/api/users" 2>/dev/null || echo "[]")
SERVICE_USER_ID=$(echo "${USERS}" | jq -r \
".[] | select(.name == \"${SERVICE_USER_NAME}\" and .is_service_user == true) | .id" 2>/dev/null || echo "")
if [ -z "${SERVICE_USER_ID}" ]; then
echo "Creating service user '${SERVICE_USER_NAME}'..."
RESPONSE=$(curl -sf -X POST \
-H "Authorization: Token ${ADMIN_PAT}" \
-H "Content-Type: application/json" \
-d "{\"name\": \"${SERVICE_USER_NAME}\", \"role\": \"admin\", \"is_service_user\": true, \"auto_groups\": []}" \
"${NETBIRD_API}/api/users")
SERVICE_USER_ID=$(echo "${RESPONSE}" | jq -r '.id')
echo " Created service user: ${SERVICE_USER_ID}"
else
echo " Service user already exists: ${SERVICE_USER_ID}"
fi
# Create PAT for the service user
echo "Creating Personal Access Token '${PAT_NAME}'..."
# Set expiration to 365 days from now
EXPIRES=$(date -u -d "+365 days" "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || \
date -u -v+365d "+%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
PAT_RESPONSE=$(curl -sf -X POST \
-H "Authorization: Token ${ADMIN_PAT}" \
-H "Content-Type: application/json" \
-d "{\"name\": \"${PAT_NAME}\", \"expires_in\": 365}" \
"${NETBIRD_API}/api/users/${SERVICE_USER_ID}/tokens")
API_TOKEN=$(echo "${PAT_RESPONSE}" | jq -r '.plain_token')
if [ -z "${API_TOKEN}" ] || [ "${API_TOKEN}" = "null" ]; then
echo "ERROR: Failed to create PAT. Response:"
echo "${PAT_RESPONSE}" | jq . 2>/dev/null || echo "${PAT_RESPONSE}"
exit 1
fi
echo " PAT created successfully"
# Store in Vault
echo "Storing API token in Vault at secret/netbird-api-token..."
ROOT_TOKEN=$(kubectl -n vault get secret vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)
kubectl exec -n vault vault-0 -- env "VAULT_TOKEN=${ROOT_TOKEN}" \
vault kv put secret/netbird-api-token api-token="${API_TOKEN}"
echo ""
echo "=== Netbird API Token Bootstrap Complete ==="
echo " Service User: ${SERVICE_USER_NAME} (${SERVICE_USER_ID})"
echo " Vault Path: secret/netbird-api-token"
echo " Expires: ~365 days"
echo ""
echo "Next steps:"
echo " 1. Run: helmfile apply (deploys operator + exporter with the token)"
echo " 2. Annotate services: kubectl annotate svc <name> -n <ns> netbird.io/expose=true"
+181
View File
@@ -0,0 +1,181 @@
#!/bin/bash
# setup-unpoller-credentials.sh
# Store Unifi credentials in Vault for Unpoller
#
# This script helps you store your Unifi controller credentials in Vault
# so that Unpoller can authenticate and collect metrics.
#
# USAGE:
# ======
# 1. With username/password:
# ./scripts/setup-unpoller-credentials.sh --user admin --pass yourpassword
#
# 2. With API key (same as External DNS):
# ./scripts/setup-unpoller-credentials.sh --use-api-key
#
# 3. Interactive mode (prompts for credentials):
# ./scripts/setup-unpoller-credentials.sh
#
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_info() { echo -e "${GREEN}${NC} $1"; }
print_warn() { echo -e "${YELLOW}${NC} $1"; }
print_error() { echo -e "${RED}${NC} $1"; }
print_success() { echo -e "${GREEN}${NC} $1"; }
# Parse command line arguments
USE_API_KEY=false
UNIFI_USER=""
UNIFI_PASS=""
while [[ $# -gt 0 ]]; do
case $1 in
--use-api-key)
USE_API_KEY=true
shift
;;
--user)
UNIFI_USER="$2"
shift 2
;;
--pass)
UNIFI_PASS="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --use-api-key Use the same API key as External DNS"
echo " --user USERNAME Unifi username (local admin account)"
echo " --pass PASSWORD Unifi password"
echo " --help, -h Show this help message"
echo ""
echo "Examples:"
echo " $0 --user admin --pass mypassword"
echo " $0 --use-api-key"
echo " $0 # Interactive mode"
exit 0
;;
*)
print_error "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
echo "=================================================="
echo " Unpoller Credentials Setup for Vault"
echo "=================================================="
echo ""
# Check if Vault is ready
print_info "Checking if Vault is ready..."
if ! kubectl -n vault get pod vault-0 >/dev/null 2>&1; then
print_error "Vault pod not found! Please deploy infrastructure first."
exit 1
fi
if ! kubectl -n vault exec vault-0 -- vault status >/dev/null 2>&1; then
print_error "Vault is not ready! Please check Vault status."
exit 1
fi
print_success "Vault is ready"
echo ""
# Get credentials
if [ "$USE_API_KEY" = true ]; then
print_info "Using API key from External DNS configuration..."
API_KEY=$(kubectl -n external-dns get secret external-dns-unifi-secret -o jsonpath='{.data.api-key}' | base64 -d)
UNIFI_USER="$API_KEY"
UNIFI_PASS="$API_KEY"
print_success "API key retrieved: ${API_KEY:0:10}..."
elif [ -z "$UNIFI_USER" ] || [ -z "$UNIFI_PASS" ]; then
# Interactive mode
print_info "No credentials provided, entering interactive mode..."
echo ""
echo "Choose authentication method:"
echo " 1) Use API key (same as External DNS)"
echo " 2) Use Unifi local admin username/password"
echo ""
read -rp "Enter choice [1-2]: " choice
case $choice in
1)
print_info "Retrieving API key from External DNS..."
API_KEY=$(kubectl -n external-dns get secret external-dns-unifi-secret -o jsonpath='{.data.api-key}' | base64 -d)
UNIFI_USER="$API_KEY"
UNIFI_PASS="$API_KEY"
print_success "API key retrieved: ${API_KEY:0:10}..."
;;
2)
read -rp "Enter Unifi username: " UNIFI_USER
read -rsp "Enter Unifi password: " UNIFI_PASS
echo ""
;;
*)
print_error "Invalid choice"
exit 1
;;
esac
fi
# Validate credentials are not empty
if [ -z "$UNIFI_USER" ] || [ -z "$UNIFI_PASS" ]; then
print_error "Credentials cannot be empty!"
exit 1
fi
echo ""
print_info "Storing credentials in Vault..."
# Store credentials in Vault
if kubectl -n vault exec vault-0 -- vault kv put secret/unpoller \
unifi-user="$UNIFI_USER" \
unifi-pass="$UNIFI_PASS" >/dev/null 2>&1; then
print_success "Credentials stored in Vault at: secret/unpoller"
else
print_error "Failed to store credentials in Vault"
exit 1
fi
echo ""
print_info "Verifying credentials were stored correctly..."
# Verify the secret exists
if kubectl -n vault exec vault-0 -- vault kv get secret/unpoller >/dev/null 2>&1; then
print_success "Credentials verified in Vault"
else
print_error "Failed to verify credentials in Vault"
exit 1
fi
echo ""
print_success "Setup complete!"
echo ""
echo "Next steps:"
echo " 1. Deploy or update Unpoller: helmfile apply"
echo " 2. Wait for Unpoller to sync credentials from Vault (via ExternalSecret)"
echo " 3. Check Unpoller logs: kubectl -n unpoller logs -l app.kubernetes.io/name=unpoller"
echo " 4. Verify metrics in Prometheus: http://prometheus.kube.huskypup.net"
echo " 5. View dashboards in Grafana: http://grafana.kube.huskypup.net"
echo ""
echo "Grafana will have 7 new Unifi dashboards:"
echo " - Unifi Access Points"
echo " - Unifi Clients"
echo " - Unifi DPI (Deep Packet Inspection)"
echo " - Unifi Gateway"
echo " - Unifi Sites"
echo " - Unifi Switches"
echo " - Unifi PDU"
echo ""
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
set -euo pipefail
# sync-gitlab-oauth.sh
# Retrieves ArgoCD OAuth provider credentials from Authentik and stores them in Vault
# This script should be run after Authentik is deployed and the ArgoCD blueprint is applied
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/argocd-oauth"
echo "=== Syncing ArgoCD OAuth Credentials from Authentik to Vault ==="
# Check if Authentik is running
# if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
# echo "ERROR: Authentik is not running. Please deploy Authentik first."
# exit 1
# fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
# Note: This uses the Authentik API via the management interface
echo "Retrieving ArgoCD OAuth credentials from Authentik..."
# Method 1: Try to get credentials directly from Authentik database
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "ArgoCD"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "ArgoCD"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If Method 1 fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Query the database for ArgoCD provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='ArgoCD';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve ArgoCD OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > ArgoCD"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing ArgoCD OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored ArgoCD OAuth credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the argocd namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n argocd argocd-oauth"
echo " kubectl get secret -n argocd argocd-oauth-secret"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== ArgoCD OAuth Sync Complete ==="
+73
View File
@@ -0,0 +1,73 @@
#!/bin/bash
# Sync GitLab admin status from Authentik "authentik Admins" group
# This script grants admin access to users who are members of "authentik Admins" in Authentik
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NAMESPACE="gitlab"
ADMIN_GROUP="authentik Admins"
echo "🔄 Syncing GitLab admin permissions from Authentik..."
# Get GitLab root password
GITLAB_ROOT_PASSWORD=$(kubectl get secret -n gitlab gitlab-gitlab-initial-root-password -o jsonpath='{.data.password}' 2>/dev/null | base64 -d || echo "")
if [ -z "$GITLAB_ROOT_PASSWORD" ]; then
echo "❌ GitLab root password not found"
echo " Please login to GitLab UI first to complete initial setup"
exit 1
fi
# Get GitLab API endpoint
GITLAB_URL="https://gitlab.kube.huskypup.net"
echo "📋 Getting list of users from Authentik '$ADMIN_GROUP' group..."
# Get Authentik database password
PGPASSWORD=$(kubectl get secret -n authentik pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query Authentik database for users in admin group
ADMIN_USERS=$(kubectl exec -n authentik pg-authentik-1 -- env PGPASSWORD="$PGPASSWORD" psql -h pg-authentik-rw -U app -d app -t -c "
SELECT DISTINCT u.email
FROM authentik_core_user u
JOIN authentik_core_user_groups ug ON u.id = ug.user_id
JOIN authentik_core_group g ON ug.group_id = g.group_uuid
WHERE g.name = '$ADMIN_GROUP' AND u.is_active = true;
" 2>/dev/null | grep -v "Defaulted" | xargs)
if [ -z "$ADMIN_USERS" ]; then
echo "⚠️ No users found in Authentik '$ADMIN_GROUP' group"
exit 0
fi
echo "✓ Found admin users: $ADMIN_USERS"
echo ""
# Create GitLab API token (using root account)
echo "🔑 Creating GitLab API token..."
# Try to login and get session
SESSION_COOKIE=$(curl -sk -c - "$GITLAB_URL/users/sign_in" | grep '_gitlab_session' | awk '{print $7}')
# Get CSRF token
CSRF_TOKEN=$(curl -sk -b "_gitlab_session=$SESSION_COOKIE" "$GITLAB_URL/users/sign_in" | grep -o 'name="authenticity_token" value="[^"]*"' | cut -d'"' -f4)
# Login as root
LOGIN_RESPONSE=$(curl -sk -b "_gitlab_session=$SESSION_COOKIE" -c - \
-X POST "$GITLAB_URL/users/sign_in" \
-d "user[login]=root&user[password]=$GITLAB_ROOT_PASSWORD&authenticity_token=$CSRF_TOKEN")
echo "⚠️ Note: GitLab CE doesn't support automatic admin assignment via API"
echo " Users must be manually promoted to admin in GitLab UI"
echo ""
echo "📝 To manually grant admin access:"
echo " 1. Login to GitLab as root: $GITLAB_URL"
echo " 2. Go to Admin Area > Users"
echo " 3. Find and edit each user: $ADMIN_USERS"
echo " 4. Check 'Admin' checkbox and save"
echo ""
echo "💡 Alternatively, run this from a GitLab Rails console:"
for email in $ADMIN_USERS; do
echo " User.find_by(email: '$email')&.update(admin: true)"
done
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
set -euo pipefail
# sync-gitlab-oauth.sh
# Retrieves GitLab OAuth provider credentials from Authentik and stores them in Vault
# This script should be run after Authentik is deployed and the GitLab blueprint is applied
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/gitlab-oauth"
echo "=== Syncing GitLab OAuth Credentials from Authentik to Vault ==="
# Check if Authentik is running
if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
echo "ERROR: Authentik is not running. Please deploy Authentik first."
exit 1
fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
# Note: This uses the Authentik API via the management interface
echo "Retrieving GitLab OAuth credentials from Authentik..."
# Method 1: Try to get credentials directly from Authentik database
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "GitLab"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "GitLab"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If Method 1 fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Query the database for GitLab provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='GitLab';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve GitLab OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > GitLab"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing GitLab OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored GitLab OAuth credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the gitlab namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n gitlab gitlab-oidc"
echo " kubectl get secret -n gitlab gitlab-oidc-secret"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== GitLab OAuth Sync Complete ==="
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
set -euo pipefail
# sync-grafana-oauth.sh
# Retrieves Grafana OAuth provider credentials from Authentik and stores them in Vault
# This script should be run after Authentik is deployed and the Grafana blueprint is applied
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/grafana-oauth"
echo "=== Syncing Grafana OAuth Credentials from Authentik to Vault ==="
# Check if Authentik is running
if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
echo "ERROR: Authentik is not running. Please deploy Authentik first."
exit 1
fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
# Note: This uses the Authentik API via the management interface
echo "Retrieving Grafana OAuth credentials from Authentik..."
# Method 1: Try to get credentials directly from Authentik database
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Grafana"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Grafana"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If Method 1 fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Query the database for Grafana provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='Grafana';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve Grafana OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > Grafana"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing Grafana OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored Grafana OAuth credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the grafana namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n grafana grafana-oauth"
echo " kubectl get secret -n grafana grafana-authentik-oauth"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== Grafana OAuth Sync Complete ==="
+113
View File
@@ -0,0 +1,113 @@
#!/bin/bash
# scripts/sync-guacamole-oauth.sh
# Sync Guacamole OAuth credentials from Authentik to Vault
set -euo pipefail
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/guacamole-oauth"
echo "=== Syncing Guacamole OAuth Credentials from Authentik to Vault ==="
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
echo "Retrieving Guacamole OAuth credentials from Authentik..."
# Try to get credentials via ak command
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "guacamole"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "guacamole"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If command fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Get PostgreSQL password
PG_PASSWORD=$(kubectl get secret -n "${AUTHENTIK_NAMESPACE}" pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query the database for guacamole provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -c postgres -- env PGPASSWORD="${PG_PASSWORD}" psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='guacamole';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve Guacamole OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > guacamole"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " kubectl exec -n vault <vault-pod> -- vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing Guacamole OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored Guacamole OAuth credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the guacamole namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n guacamole guacamole-oauth"
echo " kubectl get secret -n guacamole guacamole-oauth-secret"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== Guacamole OAuth Sync Complete ==="
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
set -euo pipefail
# sync-homeassistant-oauth.sh
# Retrieves Home Assistant OIDC provider credentials from Authentik and stores them in Vault
# This script should be run after Authentik is deployed and the Home Assistant blueprint is applied
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/homeassistant-oauth"
echo "=== Syncing Home Assistant OIDC Credentials from Authentik to Vault ==="
# Check if Authentik is running
if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
echo "ERROR: Authentik is not running. Please deploy Authentik first."
exit 1
fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
# Note: This uses the Authentik API via the management interface
echo "Retrieving Home Assistant OIDC credentials from Authentik..."
# Method 1: Try to get credentials directly from Authentik database
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Home Assistant"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Home Assistant"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If Method 1 fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Query the database for Home Assistant provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='Home Assistant';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve Home Assistant OIDC credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > Home Assistant"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing Home Assistant OIDC credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored Home Assistant OIDC credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the home-assistant namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n home-assistant homeassistant-oauth"
echo " kubectl get secret -n home-assistant homeassistant-oidc-secret"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== Home Assistant OIDC Sync Complete ==="
+113
View File
@@ -0,0 +1,113 @@
#!/bin/bash
# scripts/sync-n8n-oauth.sh
# Sync n8n OAuth credentials from Authentik to Vault
set -euo pipefail
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/n8n-oauth"
echo "=== Syncing n8n OAuth Credentials from Authentik to Vault ==="
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
echo "Retrieving n8n OAuth credentials from Authentik..."
# Try to get credentials via ak command
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "n8n"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "n8n"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If command fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Get PostgreSQL password
PG_PASSWORD=$(kubectl get secret -n "${AUTHENTIK_NAMESPACE}" pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query the database for n8n provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -c postgres -- env PGPASSWORD="${PG_PASSWORD}" psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='n8n';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve n8n OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at https://auth.kube.huskypup.net"
echo "2. Navigate to: Applications > Providers > n8n"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " kubectl exec -n vault <vault-pod> -- vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing n8n OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored n8n OAuth credentials in Vault"
echo ""
echo "The External Secret Operator will now sync these credentials to the n8n namespace."
echo "You can verify with:"
echo " kubectl get externalsecret -n n8n n8n-oauth"
echo " kubectl get secret -n n8n n8n-oauth-secret"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== n8n OAuth Sync Complete ==="
+147
View File
@@ -0,0 +1,147 @@
#!/bin/bash
set -euo pipefail
# sync-netbird-oauth.sh
# Retrieves Netbird OAuth provider credentials from Authentik and stores them in Vault
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/netbird-oauth"
echo "=== Syncing Netbird OAuth Credentials from Authentik to Vault ==="
# Check if Authentik is running
if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
echo "ERROR: Authentik is not running. Please deploy Authentik first."
exit 1
fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
echo "Retrieving Netbird OAuth credentials from Authentik..."
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Netbird"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Netbird"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# Fallback: query PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
PG_PASS=$(kubectl -n "${AUTHENTIK_NAMESPACE}" get secret pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- env PGPASSWORD="${PG_PASS}" psql -U app -d app -h localhost -t -c \
"SELECT o.client_id, o.client_secret FROM authentik_providers_oauth2_oauth2provider o JOIN authentik_core_provider p ON o.provider_ptr_id = p.id WHERE p.name='Netbird';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "WARNING: Netbird OAuth provider not yet created by blueprint. Will retry on next sync."
exit 0
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..."
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Create Authentik service account and API token for Netbird IDP management
echo "Ensuring Netbird service account and API token exist in Authentik..."
SA_TOKEN=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak shell -c "
from authentik.core.models import Token, User, Group
try:
user = User.objects.get(username='netbird-service')
except User.DoesNotExist:
user = User.objects.create(
username='netbird-service',
name='Netbird Service Account',
type='service_account',
is_active=True,
path='goauthentik.io/service-accounts'
)
# Add to authentik Admins group (required for API access)
try:
admins = Group.objects.get(name='authentik Admins')
admins.users.add(user)
except Group.DoesNotExist:
pass
try:
token = Token.objects.get(user=user, identifier='netbird-idp-manager')
except Token.DoesNotExist:
token = Token.objects.create(
user=user, identifier='netbird-idp-manager', intent='app_password', expiring=False
)
print(token.key)
" 2>&1 | tail -1)
if [ -z "$SA_TOKEN" ] || [[ "$SA_TOKEN" == *"Error"* ]]; then
echo "WARNING: Could not create Netbird service account token. IDP user sync will not work."
SA_TOKEN="placeholder"
SA_USERNAME="netbird-service"
else
echo " Service account token: ${SA_TOKEN:0:10}..."
SA_USERNAME="netbird-service"
fi
# Store credentials in Vault
echo "Storing Netbird OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- env "VAULT_TOKEN=$(kubectl -n vault get secret vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)" \
vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}" \
service-username="${SA_USERNAME}" \
service-password="${SA_TOKEN}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored Netbird OAuth credentials in Vault"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== Netbird OAuth Sync Complete ==="
echo "NOTE: Netbird IDP user sync (cache warming) may show a 400 error."
echo "This is a known Netbird/Authentik incompatibility (Netbird uses ROPC grant"
echo "which Authentik does not support). OIDC login still works correctly."
+190
View File
@@ -0,0 +1,190 @@
#!/bin/bash
# scripts/sync-nextcloud-oauth.sh
# Sync Nextcloud OAuth credentials from Authentik to Vault and configure user_oidc provider
set -euo pipefail
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
NEXTCLOUD_NAMESPACE="nextcloud"
VAULT_SECRET_PATH="secret/nextcloud-oauth"
AUTHENTIK_URL="https://auth.kube.huskypup.net"
NEXTCLOUD_URL="https://nextcloud.kube.huskypup.net"
echo "=== Syncing Nextcloud OAuth Credentials from Authentik to Vault ==="
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
echo "Retrieving Nextcloud OAuth credentials from Authentik..."
# Try to get credentials via ak command
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Nextcloud"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
CLIENT_SECRET=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Nextcloud"' | grep '"client_secret"' | cut -d'"' -f4 || echo "")
# If command fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Get PostgreSQL password
PG_PASSWORD=$(kubectl get secret -n "${AUTHENTIK_NAMESPACE}" pg-authentik-app -o jsonpath='{.data.password}' | base64 -d)
# Query the database for Nextcloud provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -c postgres -- env PGPASSWORD="${PG_PASSWORD}" psql -U authentik -d authentik -t -c \
"SELECT client_id, client_secret FROM authentik_providers_oauth2_oauth2provider WHERE name='Nextcloud';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | awk '{print $1}' | tr -d ' ')
CLIENT_SECRET=$(echo "$PROVIDER_DATA" | awk '{print $3}' | tr -d ' ')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ] || [ -z "$CLIENT_SECRET" ]; then
echo "ERROR: Failed to retrieve Nextcloud OAuth credentials from Authentik"
echo ""
echo "Manual steps required:"
echo "1. Access Authentik admin panel at ${AUTHENTIK_URL}"
echo "2. Navigate to: Applications > Providers > Nextcloud"
echo "3. Copy the Client ID and Client Secret"
echo "4. Store them in Vault manually with:"
echo " kubectl exec -n vault <vault-pod> -- vault kv put ${VAULT_SECRET_PATH} client-id=\"<CLIENT_ID>\" client-secret=\"<CLIENT_SECRET>\""
exit 1
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
echo " Client Secret: ${CLIENT_SECRET:0:10}..." # Only show first 10 chars
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing Nextcloud OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}" \
client-secret="${CLIENT_SECRET}"
if [ $? -eq 0 ]; then
echo "✓ Successfully stored Nextcloud OAuth credentials in Vault"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
# Wait for External Secret to sync
echo ""
echo "Waiting for External Secret to sync credentials to Nextcloud namespace..."
sleep 5
# Verify External Secret synced
kubectl wait --for=condition=Ready externalsecret/nextcloud-oauth -n "${NEXTCLOUD_NAMESPACE}" --timeout=30s || {
echo "WARNING: External Secret may not be ready yet. Continuing anyway..."
}
# Wait for Nextcloud to be ready
echo "Waiting for Nextcloud to be ready..."
kubectl -n "${NEXTCLOUD_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=nextcloud --timeout=60s || {
echo "WARNING: Nextcloud may not be fully ready yet. Continuing anyway..."
}
# Configure Nextcloud user_oidc provider
echo ""
echo "=== Configuring Nextcloud OIDC Provider ==="
# Get Nextcloud pod
NEXTCLOUD_POD=$(kubectl get pods -n "${NEXTCLOUD_NAMESPACE}" -l app.kubernetes.io/name=nextcloud -o jsonpath='{.items[0].metadata.name}')
if [ -z "$NEXTCLOUD_POD" ]; then
echo "ERROR: Could not find Nextcloud pod"
exit 1
fi
echo "Found Nextcloud pod: ${NEXTCLOUD_POD}"
# Check if provider already exists
EXISTING_PROVIDER=$(kubectl exec -n "${NEXTCLOUD_NAMESPACE}" "${NEXTCLOUD_POD}" -- php occ user_oidc:provider 2>/dev/null | grep -c "Authentik" || true)
if [ "$EXISTING_PROVIDER" -gt 0 ]; then
echo "Updating existing Authentik OIDC provider..."
kubectl exec -n "${NEXTCLOUD_NAMESPACE}" "${NEXTCLOUD_POD}" -- php occ user_oidc:provider Authentik \
--clientid="${CLIENT_ID}" \
--clientsecret="${CLIENT_SECRET}" \
--discoveryuri="${AUTHENTIK_URL}/application/o/nextcloud/.well-known/openid-configuration" \
--scope="openid email profile" \
--mapping-uid="preferred_username" \
--mapping-display-name="name" \
--mapping-email="email" \
--unique-uid=0
else
echo "Creating new Authentik OIDC provider..."
kubectl exec -n "${NEXTCLOUD_NAMESPACE}" "${NEXTCLOUD_POD}" -- php occ user_oidc:provider Authentik \
--clientid="${CLIENT_ID}" \
--clientsecret="${CLIENT_SECRET}" \
--discoveryuri="${AUTHENTIK_URL}/application/o/nextcloud/.well-known/openid-configuration" \
--scope="openid email profile" \
--mapping-uid="preferred_username" \
--mapping-display-name="name" \
--mapping-email="email" \
--unique-uid=0
fi
if [ $? -eq 0 ]; then
echo "✓ Successfully configured Nextcloud OIDC provider"
else
echo "ERROR: Failed to configure Nextcloud OIDC provider"
exit 1
fi
# Verify configuration
echo ""
echo "Verifying OIDC provider configuration..."
kubectl exec -n "${NEXTCLOUD_NAMESPACE}" "${NEXTCLOUD_POD}" -- php occ user_oidc:provider Authentik
echo ""
echo "=== Nextcloud OAuth Configuration Complete ==="
echo ""
echo "You can now:"
echo "1. Visit ${NEXTCLOUD_URL}"
echo "2. Click 'Log in with Authentik' button on the login page"
echo "3. Authenticate using your Authentik credentials"
echo ""
echo "To verify the configuration:"
echo " kubectl get externalsecret -n ${NEXTCLOUD_NAMESPACE} nextcloud-oauth"
echo " kubectl get secret -n ${NEXTCLOUD_NAMESPACE} nextcloud-oauth-secret"
echo " kubectl exec -n ${NEXTCLOUD_NAMESPACE} ${NEXTCLOUD_POD} -- php occ user_oidc:provider"
+104
View File
@@ -0,0 +1,104 @@
#!/bin/bash
set -euo pipefail
# sync-percona-everest-oauth.sh
# Retrieves Percona Everest OIDC provider credentials from Authentik and stores them in Vault
# Everest uses PKCE (public client) so the client_secret is not strictly required,
# but we store it in Vault for reference and potential future use.
AUTHENTIK_NAMESPACE="authentik"
VAULT_NAMESPACE="vault"
VAULT_SECRET_PATH="secret/percona-everest-oauth"
echo "=== Syncing Percona Everest OAuth Credentials from Authentik to Vault ==="
# Check if Authentik is running
if ! kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --no-headers 2>/dev/null | grep -q Running; then
echo "ERROR: Authentik is not running. Please deploy Authentik first."
exit 1
fi
# Wait for Authentik to be fully ready
echo "Waiting for Authentik to be ready..."
kubectl -n "${AUTHENTIK_NAMESPACE}" wait --for=condition=Ready pod -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server --timeout=60s || {
echo "WARNING: Authentik may not be fully ready yet. Continuing anyway..."
}
# Get Authentik pod name
AUTHENTIK_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l app.kubernetes.io/name=authentik,app.kubernetes.io/component=server -o jsonpath='{.items[0].metadata.name}')
if [ -z "$AUTHENTIK_POD" ]; then
echo "ERROR: Could not find Authentik server pod"
exit 1
fi
echo "Found Authentik pod: ${AUTHENTIK_POD}"
# Retrieve OAuth2 provider credentials from Authentik
echo "Retrieving Percona Everest OAuth credentials from Authentik..."
# Method 1: Try to get credentials directly from Authentik CLI
CLIENT_ID=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${AUTHENTIK_POD}" -- ak list_providers --type oauth2 2>/dev/null | grep -A 20 '"name": "Percona Everest"' | grep '"client_id"' | cut -d'"' -f4 || echo "")
# If Method 1 fails, try using PostgreSQL directly
if [ -z "$CLIENT_ID" ]; then
echo "Attempting to retrieve credentials from Authentik PostgreSQL database..."
CNPG_POD=$(kubectl get pods -n "${AUTHENTIK_NAMESPACE}" -l cnpg.io/cluster=pg-authentik,role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -z "$CNPG_POD" ]; then
echo "ERROR: Could not find Authentik PostgreSQL pod"
exit 1
fi
# Query the database for Percona Everest provider credentials
PROVIDER_DATA=$(kubectl exec -n "${AUTHENTIK_NAMESPACE}" "${CNPG_POD}" -- psql -U authentik -d authentik -t -c \
"SELECT client_id FROM authentik_providers_oauth2_oauth2provider WHERE name='Percona Everest';" 2>/dev/null || echo "")
if [ -n "$PROVIDER_DATA" ]; then
CLIENT_ID=$(echo "$PROVIDER_DATA" | tr -d ' ' | tr -d '\n')
fi
fi
# Validate credentials were retrieved
if [ -z "$CLIENT_ID" ]; then
echo "WARNING: Could not retrieve Percona Everest OAuth credentials from Authentik"
echo "The OIDC provider may not be configured yet (blueprint not processed)."
echo "Everest will use local admin authentication until OIDC is available."
exit 0
fi
echo "Successfully retrieved credentials:"
echo " Client ID: ${CLIENT_ID}"
# Check if Vault is unsealed and ready
echo "Checking Vault status..."
VAULT_POD=$(kubectl get pods -n "${VAULT_NAMESPACE}" -l app.kubernetes.io/name=vault -o jsonpath='{.items[0].metadata.name}')
if [ -z "$VAULT_POD" ]; then
echo "ERROR: Vault pod not found"
exit 1
fi
VAULT_STATUS=$(kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault status -format=json 2>/dev/null || echo "{}")
SEALED=$(echo "$VAULT_STATUS" | grep -o '"sealed":[^,}]*' | cut -d':' -f2 | tr -d ' ')
if [ "$SEALED" = "true" ]; then
echo "ERROR: Vault is sealed. Please unseal Vault first."
exit 1
fi
# Store credentials in Vault
echo "Storing Percona Everest OAuth credentials in Vault at ${VAULT_SECRET_PATH}..."
kubectl exec -n "${VAULT_NAMESPACE}" "${VAULT_POD}" -- vault kv put "${VAULT_SECRET_PATH}" \
client-id="${CLIENT_ID}"
if [ $? -eq 0 ]; then
echo "Successfully stored Percona Everest OAuth credentials in Vault"
else
echo "ERROR: Failed to store credentials in Vault"
exit 1
fi
echo ""
echo "=== Percona Everest OAuth Sync Complete ==="
+90
View File
@@ -0,0 +1,90 @@
#!/bin/bash
set -e
export TALOSCONFIG=/home/scooby/Talosv3/talosconfig
echo "=== Talos iSCSI Bootstrap Script ==="
echo ""
echo "This script configures Talos nodes for Longhorn by:"
echo "1. Enabling control plane scheduling"
echo "2. Adding Longhorn volume mounts"
echo "3. Upgrading to factory image with iscsi-tools"
echo ""
# Step 1: Enable scheduling on control planes
echo "Step 1/4: Enabling scheduling on control planes..."
cat > /tmp/allow-scheduling.yaml <<EOF
cluster:
allowSchedulingOnControlPlanes: true
EOF
talosctl patch mc -p @/tmp/allow-scheduling.yaml --nodes 172.28.101.41,172.28.101.42,172.28.101.43,172.28.101.44
echo "✅ Scheduling enabled"
# Step 2: Add Longhorn volume mounts
echo ""
echo "Step 2/4: Adding Longhorn volume mounts..."
cat > /tmp/longhorn-volume.patch.yaml <<EOF
machine:
kubelet:
extraMounts:
- destination: /var/lib/longhorn
type: bind
source: /var/lib/longhorn
options:
- bind
- rshared
- rw
EOF
talosctl patch mc --nodes 172.28.101.41,172.28.101.42,172.28.101.43,172.28.101.44 --patch @/tmp/longhorn-volume.patch.yaml
echo "✅ Longhorn mounts configured"
# Step 3: Remove old extension references
echo ""
echo "Step 3/4: Removing deprecated extension references..."
cat > /tmp/clear-extensions.yaml <<EOF
- op: remove
path: /machine/install/extensions
EOF
talosctl patch mc --patch @/tmp/clear-extensions.yaml --nodes 172.28.101.41,172.28.101.42,172.28.101.43,172.28.101.44 2>/dev/null || true
echo "✅ Old extensions removed"
# Step 4: Upgrade to factory image with iscsi-tools
echo ""
echo "Step 4/4: Upgrading to Talos with iscsi-tools (this will take several minutes)..."
echo "Factory image includes: iscsi-tools, util-linux-tools"
echo ""
SCHEMATIC_ID="613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245"
TALOS_VERSION="v1.9.4"
echo "Upgrading nodes one at a time to: factory.talos.dev/installer/$SCHEMATIC_ID:$TALOS_VERSION"
for node in 172.28.101.41 172.28.101.42 172.28.101.43 172.28.101.44; do
echo ""
echo "Upgrading $node..."
talosctl upgrade --nodes $node \
--image factory.talos.dev/installer/$SCHEMATIC_ID:$TALOS_VERSION \
--preserve --wait --timeout 10m || echo "⚠️ Node $node upgrade encountered issues, continuing..."
echo "Waiting 30s for node to stabilize..."
sleep 30
done
echo ""
echo "=== Waiting for all nodes to be Ready ==="
kubectl wait --for=condition=Ready nodes --all --timeout=300s
echo ""
echo "=== Verifying iSCSI installation ==="
talosctl get extensions -n 172.28.101.41
echo ""
echo "✅ Talos iSCSI bootstrap complete!"
echo ""
echo "Next steps:"
echo " cd /home/scooby/Homelabv5"
echo " ./scripts/bootstrap-crds.sh"
echo " helmfile apply"
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
# Teslamate Bootstrap - Auto-configure PostgreSQL with ESO
set -euo pipefail
echo "=== Teslamate Bootstrap - Fully Automated ==="
# Change to the apps directory for relative paths
cd "$(dirname "$0")/../apps" || exit 1
NS=teslamate
CLUSTER=pg-teslamate
# 1) Ensure namespace
kubectl get ns "${NS}" >/dev/null 2>&1 || kubectl create ns "${NS}"
# 2) Apply CNPG cluster (auto-generates DB password)
echo "Applying Teslamate PostgreSQL cluster..."
kubectl apply -f teslamate/cnpg-cluster.yaml
# 3) Wait for CNPG cluster
echo "Waiting for CNPG cluster ${CLUSTER}..."
kubectl -n "${NS}" wait --for=condition=Ready "cluster/${CLUSTER}" --timeout=300s
# 3.5) Set up PostgreSQL extensions and grant superuser
echo "Setting up PostgreSQL extensions for Teslamate..."
PRIMARY_POD=$(kubectl get pod -n "${NS}" -l cnpg.io/cluster="${CLUSTER}",role=primary -o jsonpath='{.items[0].metadata.name}')
if [ -n "$PRIMARY_POD" ]; then
echo "Granting superuser to teslamate user..."
kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -c "ALTER USER teslamate WITH SUPERUSER;" 2>/dev/null || echo " (user may already have superuser)"
echo "Creating required PostgreSQL extensions..."
kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -d teslamate -c "CREATE EXTENSION IF NOT EXISTS cube;" 2>/dev/null || echo " (cube extension may already exist)"
kubectl exec -n "${NS}" "$PRIMARY_POD" -- psql -U postgres -d teslamate -c "CREATE EXTENSION IF NOT EXISTS earthdistance;" 2>/dev/null || echo " (earthdistance extension may already exist)"
echo "✅ PostgreSQL extensions configured"
else
echo "⚠️ Warning: Could not find primary pod, skipping extension setup"
fi
# 4) Apply ESO secrets (auto-rotate DB passwords)
echo "Applying Teslamate ESO secrets (auto-generates passwords)..."
kubectl apply -f teslamate/cnpg-secrets.yaml
# 5) Wait for password to be generated
echo "Waiting for ESO to generate database password..."
for i in {1..30}; do
if kubectl -n "${NS}" get secret pg-teslamate-app >/dev/null 2>&1; then
echo "✅ Database password generated"
break
fi
echo " waiting for secret... (attempt $i/30)"
sleep 2
done
# 6) Initialize encryption key in Vault if it doesn't exist
echo "Initializing encryption key in Vault..."
# Get Vault root token from secret
VAULT_ROOT_TOKEN=$(kubectl -n vault get secret vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' 2>/dev/null | base64 -d || echo "")
if [ -z "$VAULT_ROOT_TOKEN" ]; then
echo "⚠️ Warning: Could not retrieve Vault root token from secret vault-init-keys"
echo "Skipping encryption key creation. You'll need to create it manually."
echo ""
echo "To create the encryption key manually:"
echo " ENCRYPTION_KEY=\$(openssl rand -base64 32)"
echo " kubectl exec -n vault vault-0 -- vault kv put secret/teslamate/config encryption_key=\"\${ENCRYPTION_KEY}\""
echo ""
# Continue anyway - the ExternalSecret will show an error if the key doesn't exist
else
# Check if the secret already exists in Vault
if kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv get secret/teslamate/config >/dev/null 2>&1; then
echo "✅ Encryption key already exists in Vault"
else
echo "Generating new encryption key..."
ENCRYPTION_KEY=$(openssl rand -base64 32)
# Write to Vault using root token
if kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv put secret/teslamate/config encryption_key="${ENCRYPTION_KEY}"; then
echo "✅ Encryption key stored in Vault"
else
echo "❌ Failed to write to Vault"
echo "Please create the encryption key manually:"
echo " kubectl exec -n vault vault-0 -- env VAULT_TOKEN=\"${VAULT_ROOT_TOKEN}\" vault kv put secret/teslamate/config encryption_key=\"${ENCRYPTION_KEY}\""
exit 1
fi
fi
fi
# 7) Apply Teslamate config ExternalSecret
echo "Applying Teslamate config ExternalSecret..."
kubectl apply -f teslamate/external-secret.yaml
# 8) Wait for teslamate-config-secret to be created
echo "Waiting for teslamate-config-secret to be synced from Vault..."
SECRET_SYNCED=false
for i in {1..30}; do
if kubectl -n "${NS}" get secret teslamate-config-secret >/dev/null 2>&1; then
echo "✅ teslamate-config-secret synced successfully"
SECRET_SYNCED=true
break
fi
echo " waiting for secret... (attempt $i/30)"
sleep 2
done
# 9) If ESO failed to sync, create secret manually as fallback
if [ "$SECRET_SYNCED" = "false" ]; then
echo "⚠️ ExternalSecret failed to sync (ClusterSecretStore issue)"
echo "Creating teslamate-config-secret manually from Vault..."
if [ -n "$VAULT_ROOT_TOKEN" ]; then
ENCRYPTION_KEY=$(kubectl exec -n vault vault-0 -- env VAULT_TOKEN="${VAULT_ROOT_TOKEN}" vault kv get -field=encryption_key secret/teslamate/config 2>/dev/null || echo "")
if [ -n "$ENCRYPTION_KEY" ]; then
kubectl create secret generic teslamate-config-secret -n "${NS}" \
--from-literal=encryption-key="${ENCRYPTION_KEY}" \
--dry-run=client -o yaml | kubectl apply -f -
echo "✅ Created teslamate-config-secret manually"
else
echo "❌ Failed to retrieve encryption key from Vault"
exit 1
fi
else
echo "❌ Cannot create secret manually - no Vault token"
exit 1
fi
fi
echo "✅ Teslamate bootstrap complete - fully automated!"
+233
View File
@@ -0,0 +1,233 @@
---
# V1 PVC (traditional iSCSI engine)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: benchmark-v1-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn # V1 engine (default)
resources:
requests:
storage: 50Gi
---
# V2 PVC (SPDK engine)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: benchmark-v2-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn-v2 # V2 SPDK engine
resources:
requests:
storage: 50Gi
---
# Benchmark scripts ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: v1-v2-benchmark-scripts
namespace: default
data:
run-benchmarks.sh: |
#!/bin/sh
set -e
ENGINE=$1
DATA_DIR=$2
echo "=========================================="
echo "Longhorn $ENGINE Data Engine Benchmark"
echo "=========================================="
echo "Test file: $DATA_DIR/testfile (10GB)"
echo ""
# Sequential Write
echo "1. Sequential Write Test..."
fio --name=seq-write \
--filename=$DATA_DIR/testfile \
--size=10G \
--rw=write \
--bs=1M \
--direct=1 \
--numjobs=1 \
--time_based \
--runtime=60 \
--group_reporting \
--output-format=normal | tee /tmp/seq-write.log
SEQ_WRITE_BW=$(grep "WRITE:" /tmp/seq-write.log | awk '{print $2}' | sed 's/bw=//;s/,//')
SEQ_WRITE_IOPS=$(grep "WRITE:" /tmp/seq-write.log | awk '{print $4}' | sed 's/IOPS=//;s/,//')
echo ""
echo "=========================================="
echo ""
# Sequential Read
echo "2. Sequential Read Test..."
fio --name=seq-read \
--filename=$DATA_DIR/testfile \
--rw=read \
--bs=1M \
--direct=1 \
--numjobs=1 \
--time_based \
--runtime=60 \
--group_reporting \
--output-format=normal | tee /tmp/seq-read.log
SEQ_READ_BW=$(grep "READ:" /tmp/seq-read.log | awk '{print $2}' | sed 's/bw=//;s/,//')
SEQ_READ_IOPS=$(grep "READ:" /tmp/seq-read.log | awk '{print $4}' | sed 's/IOPS=//;s/,//')
echo ""
echo "=========================================="
echo ""
# Random Write
echo "3. Random Write Test (4K blocks)..."
fio --name=rand-write \
--filename=$DATA_DIR/testfile \
--rw=randwrite \
--bs=4k \
--direct=1 \
--numjobs=4 \
--time_based \
--runtime=60 \
--group_reporting \
--output-format=normal | tee /tmp/rand-write.log
RAND_WRITE_IOPS=$(grep "WRITE:" /tmp/rand-write.log | awk '{print $4}' | sed 's/IOPS=//;s/,//')
RAND_WRITE_BW=$(grep "WRITE:" /tmp/rand-write.log | awk '{print $2}' | sed 's/bw=//;s/,//')
echo ""
echo "=========================================="
echo ""
# Random Read
echo "4. Random Read Test (4K blocks)..."
fio --name=rand-read \
--filename=$DATA_DIR/testfile \
--rw=randread \
--bs=4k \
--direct=1 \
--numjobs=4 \
--time_based \
--runtime=60 \
--group_reporting \
--output-format=normal | tee /tmp/rand-read.log
RAND_READ_IOPS=$(grep "READ:" /tmp/rand-read.log | awk '{print $4}' | sed 's/IOPS=//;s/,//')
RAND_READ_BW=$(grep "READ:" /tmp/rand-read.log | awk '{print $2}' | sed 's/bw=//;s/,//')
echo ""
echo "=========================================="
echo "$ENGINE Engine Results Summary"
echo "=========================================="
echo "Sequential Write: $SEQ_WRITE_BW ($SEQ_WRITE_IOPS IOPS)"
echo "Sequential Read: $SEQ_READ_BW ($SEQ_READ_IOPS IOPS)"
echo "Random Write: $RAND_WRITE_BW ($RAND_WRITE_IOPS IOPS)"
echo "Random Read: $RAND_READ_BW ($RAND_READ_IOPS IOPS)"
echo "=========================================="
---
# V1 Benchmark Job
apiVersion: batch/v1
kind: Job
metadata:
name: benchmark-v1
namespace: default
spec:
ttlSecondsAfterFinished: 3600
template:
metadata:
labels:
app: benchmark
engine: v1
spec:
restartPolicy: Never
containers:
- name: fio
image: ljishen/fio:latest
command: ["/bin/sh"]
args: ["/scripts/run-benchmarks.sh", "V1", "/data"]
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
volumeMounts:
- name: data
mountPath: /data
- name: scripts
mountPath: /scripts
securityContext:
runAsNonRoot: false
runAsUser: 0
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumes:
- name: data
persistentVolumeClaim:
claimName: benchmark-v1-pvc
- name: scripts
configMap:
name: v1-v2-benchmark-scripts
defaultMode: 0755
---
# V2 Benchmark Job
apiVersion: batch/v1
kind: Job
metadata:
name: benchmark-v2
namespace: default
spec:
ttlSecondsAfterFinished: 3600
template:
metadata:
labels:
app: benchmark
engine: v2
spec:
restartPolicy: Never
containers:
- name: fio
image: ljishen/fio:latest
command: ["/bin/sh"]
args: ["/scripts/run-benchmarks.sh", "V2", "/data"]
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
volumeMounts:
- name: data
mountPath: /data
- name: scripts
mountPath: /scripts
securityContext:
runAsNonRoot: false
runAsUser: 0
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumes:
- name: data
persistentVolumeClaim:
claimName: benchmark-v2-pvc
- name: scripts
configMap:
name: v1-v2-benchmark-scripts
defaultMode: 0755
+199
View File
@@ -0,0 +1,199 @@
#!/bin/bash
# scripts/validate-n8n-deployment.sh
# Validate n8n deployment and SCCM automation setup
set -euo pipefail
echo "=========================================="
echo "n8n Deployment Validation"
echo "=========================================="
echo ""
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
validate_step() {
local step_name=$1
local command=$2
echo -n "Checking $step_name... "
if eval "$command" &>/dev/null; then
echo -e "${GREEN}✓ PASS${NC}"
return 0
else
echo -e "${RED}✗ FAIL${NC}"
return 1
fi
}
validate_step_with_output() {
local step_name=$1
local command=$2
echo "Checking $step_name..."
if output=$(eval "$command" 2>&1); then
echo -e "${GREEN}✓ PASS${NC}"
echo "$output"
return 0
else
echo -e "${RED}✗ FAIL${NC}"
echo "$output"
return 1
fi
}
# Track failures
FAILURES=0
# 1. Check namespace exists
if ! validate_step "n8n namespace exists" "kubectl get namespace n8n"; then
((FAILURES++))
echo -e "${YELLOW} → Run: helmfile -l name=n8n apply${NC}"
fi
# 2. Check external secrets
echo ""
echo "Checking External Secrets..."
if kubectl get namespace n8n &>/dev/null; then
if ! validate_step " PostgreSQL secret" "kubectl get externalsecret -n n8n n8n-postgresql"; then
((FAILURES++))
echo -e "${YELLOW} → Run: kubectl apply -f kubernetes/apps/n8n/external-secret.yaml${NC}"
fi
if ! validate_step " Config secret" "kubectl get externalsecret -n n8n n8n-config"; then
((FAILURES++))
echo -e "${YELLOW} → Run: kubectl apply -f kubernetes/apps/n8n/external-secret.yaml${NC}"
fi
# Check if secrets are synced
if ! validate_step " PostgreSQL secret synced" "kubectl get secret -n n8n n8n-postgresql-secret"; then
((FAILURES++))
echo -e "${YELLOW} → Ensure Vault has secrets. Run: ./scripts/init-n8n-secrets.sh${NC}"
fi
if ! validate_step " Config secret synced" "kubectl get secret -n n8n n8n-config-secret"; then
((FAILURES++))
echo -e "${YELLOW} → Ensure Vault has secrets. Run: ./scripts/init-n8n-secrets.sh${NC}"
fi
fi
# 3. Check PostgreSQL deployment
echo ""
echo "Checking PostgreSQL..."
if ! validate_step " PostgreSQL StatefulSet" "kubectl get statefulset -n n8n n8n-postgresql"; then
((FAILURES++))
fi
if kubectl get statefulset -n n8n n8n-postgresql &>/dev/null; then
if ! validate_step " PostgreSQL pod ready" "kubectl wait --for=condition=ready pod -n n8n -l app.kubernetes.io/name=postgresql --timeout=10s"; then
((FAILURES++))
echo -e "${YELLOW} → Check: kubectl logs -n n8n n8n-postgresql-0${NC}"
fi
fi
# 4. Check n8n deployment
echo ""
echo "Checking n8n deployment..."
if ! validate_step " n8n deployment exists" "kubectl get deployment -n n8n n8n"; then
((FAILURES++))
else
if ! validate_step " n8n pod ready" "kubectl wait --for=condition=ready pod -n n8n -l app.kubernetes.io/name=n8n --timeout=10s"; then
((FAILURES++))
echo -e "${YELLOW} → Check: kubectl logs -n n8n deployment/n8n${NC}"
fi
fi
# 5. Check service
echo ""
if ! validate_step "n8n service exists" "kubectl get service -n n8n n8n"; then
((FAILURES++))
fi
# 6. Check ingress
echo ""
if ! validate_step "n8n ingress exists" "kubectl get ingress -n n8n"; then
((FAILURES++))
fi
# 7. Check ingress hostname
echo ""
if kubectl get ingress -n n8n &>/dev/null; then
INGRESS_HOST=$(kubectl get ingress -n n8n -o jsonpath='{.items[0].spec.rules[0].host}' 2>/dev/null || echo "")
if [ -n "$INGRESS_HOST" ]; then
echo -e "n8n ingress hostname: ${GREEN}$INGRESS_HOST${NC}"
else
echo -e "${RED}✗ No ingress hostname found${NC}"
((FAILURES++))
fi
fi
# 8. Check TLS certificate
echo ""
if ! validate_step "n8n TLS certificate" "kubectl get certificate -n n8n n8n-tls"; then
((FAILURES++))
else
CERT_READY=$(kubectl get certificate -n n8n n8n-tls -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "False")
if [ "$CERT_READY" = "True" ]; then
echo -e " Certificate status: ${GREEN}Ready${NC}"
else
echo -e " Certificate status: ${YELLOW}Not Ready${NC}"
echo -e "${YELLOW} → Check: kubectl describe certificate -n n8n n8n-tls${NC}"
fi
fi
# 9. Check PVC
echo ""
if ! validate_step "n8n PVC exists" "kubectl get pvc -n n8n"; then
((FAILURES++))
fi
# 10. Test n8n health
echo ""
echo "Testing n8n connectivity..."
if [ -n "$INGRESS_HOST" ]; then
if HTTP_CODE=$(curl -s -k -o /dev/null -w "%{http_code}" "https://$INGRESS_HOST" 2>/dev/null); then
if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 302 ]; then
echo -e " HTTP response: ${GREEN}$HTTP_CODE${NC}"
else
echo -e " HTTP response: ${YELLOW}$HTTP_CODE${NC}"
fi
else
echo -e "${YELLOW} → Could not connect to $INGRESS_HOST${NC}"
fi
fi
# Summary
echo ""
echo "=========================================="
if [ $FAILURES -eq 0 ]; then
echo -e "${GREEN}✓ All checks passed!${NC}"
echo ""
echo "n8n is ready at: https://$INGRESS_HOST"
echo ""
echo "Next steps:"
echo "1. Access n8n and create admin account"
echo "2. Install SCCM API service on Windows: sccm-automation/Install-SCCMAPIService.ps1"
echo "3. Import workflow: sccm-automation/firefox-automation-workflow.json"
echo "4. Configure credentials in n8n"
echo "5. Activate the workflow"
echo ""
echo "See: sccm-automation/QUICKSTART.md for detailed setup"
else
echo -e "${RED}$FAILURES check(s) failed${NC}"
echo ""
echo "Please review the errors above and take corrective action."
echo ""
echo "Common fixes:"
echo "1. Initialize secrets: ./scripts/init-n8n-secrets.sh"
echo "2. Deploy n8n: helmfile -l name=n8n apply"
echo "3. Check logs: kubectl logs -n n8n deployment/n8n"
echo "4. Check events: kubectl get events -n n8n --sort-by='.lastTimestamp'"
fi
echo "=========================================="
exit $FAILURES
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -euo pipefail
VAULT_NS="${VAULT_NS:-vault}"
SECRET_NAME="${SECRET_NAME:-vault-init-keys}"
KV_MOUNT="${KV_MOUNT:-secret}"
JOB_NS_FOR_ROLE="${JOB_NS_FOR_ROLE:-auth-proxy}"
JOB_SA_FOR_ROLE="${JOB_SA_FOR_ROLE:-oauth2-bootstrap}"
POLICY_NAME="${POLICY_NAME:-oauth2-writer}"
ROLE_NAME="${ROLE_NAME:-eso-writer}"
# --- prereqs ---
command -v kubectl >/dev/null || { echo "kubectl not found"; exit 1; }
command -v vault >/dev/null || { echo "vault CLI not found"; exit 1; }
command -v jq >/dev/null || { echo "jq not found"; exit 1; }
# --- port-forward Vault locally ---
echo "==> Port-forwarding Vault service (ctrl-c in another terminal to stop when done)"
kubectl -n "$VAULT_NS" port-forward svc/vault 8200:8200 >/dev/null 2>&1 &
pf_pid=$!
trap 'kill $pf_pid >/dev/null 2>&1 || true' EXIT
sleep 2
export VAULT_ADDR="http://127.0.0.1:8200"
# --- check init/seal status ---
status_json="$(vault status -format=json || true)"
initialized="$(jq -r '.initialized // empty' <<<"$status_json")"
sealed="$(jq -r '.sealed // empty' <<<"$status_json")"
root_token=""
unseal_key=""
# pull existing secret if present
if kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" >/dev/null 2>&1; then
root_token="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d || true)"
unseal_key="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_UNSEAL_KEY}' | base64 -d || true)"
fi
# initialize if needed
if [[ "$initialized" != "true" ]]; then
echo "==> Vault not initialized; initializing..."
init_json="$(vault operator init -key-shares=1 -key-threshold=1 -format=json)"
root_token="$(jq -r .root_token <<<"$init_json")"
unseal_key="$(jq -r '.unseal_keys_b64[0]' <<<"$init_json")"
sealed="true"
echo "==> Storing root token & unseal key in Secret ${VAULT_NS}/${SECRET_NAME}"
kubectl -n "$VAULT_NS" create secret generic "$SECRET_NAME" \
--from-literal=VAULT_ROOT_TOKEN="$root_token" \
--from-literal=VAULT_UNSEAL_KEY="$unseal_key" \
--dry-run=client -o yaml | kubectl apply -f -
else
echo "==> Vault already initialized."
fi
# unseal if needed
if [[ "$sealed" == "true" ]]; then
[[ -n "$unseal_key" ]] || { echo "ERROR: sealed and no unseal key available"; exit 1; }
echo "==> Unsealing..."
vault operator unseal "$unseal_key" >/dev/null
fi
# login
if [[ -z "$root_token" ]]; then
echo "==> Reading root token from Secret..."
root_token="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)"
fi
vault login "$root_token" >/dev/null
# ensure KV v2 enabled
if ! vault secrets list -format=json | jq -e "has(\"${KV_MOUNT}/\")" >/dev/null; then
echo "==> Enabling KV v2 at ${KV_MOUNT}/"
vault secrets enable -path="$KV_MOUNT" -version=2 kv >/dev/null
fi
# configure Kubernetes auth with a reviewer token from a local SA
echo "==> Ensuring reviewer SA + binding"
kubectl -n "$VAULT_NS" get sa vault-auth >/dev/null 2>&1 || kubectl -n "$VAULT_NS" create sa vault-auth
kubectl get clusterrolebinding vault-auth-delegator >/dev/null 2>&1 || \
kubectl create clusterrolebinding vault-auth-delegator \
--clusterrole=system:auth-delegator \
--serviceaccount="${VAULT_NS}:vault-auth"
reviewer_jwt="$(kubectl -n "$VAULT_NS" create token vault-auth)"
kube_ca="$(kubectl -n kube-system get configmap kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')"
kube_host="https://kubernetes.default.svc:443"
vault auth enable kubernetes >/dev/null 2>&1 || true
vault write auth/kubernetes/config \
token_reviewer_jwt="$reviewer_jwt" \
kubernetes_host="$kube_host" \
kubernetes_ca_cert="$kube_ca" >/dev/null
# policy + role for ESO/oauth2 job
vault policy write "$POLICY_NAME" - >/dev/null <<'HCL'
path "secret/data/*" {
capabilities = ["create", "update", "read", "list"]
}
path "secret/metadata/*" {
capabilities = ["create", "update", "read", "list"]
}
HCL
vault write auth/kubernetes/role/${ROLE_NAME} \
bound_service_account_names="${JOB_SA_FOR_ROLE},external-secrets" \
bound_service_account_namespaces="${JOB_NS_FOR_ROLE},external-secrets" \
policies="${POLICY_NAME}" \
ttl="24h" >/dev/null
echo "==> Done."
echo "K8s Secret with init creds: ${VAULT_NS}/${SECRET_NAME}"
echo "IMPORTANT: back these up securely and delete the Secret when youre comfortable:"
echo " kubectl -n ${VAULT_NS} delete secret ${SECRET_NAME}"
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# verify-gitlab-oidc.sh
# Verify GitLab OIDC configuration and connectivity to Authentik
set -euo pipefail
echo "=== GitLab OIDC Configuration Verification ==="
echo ""
# Check if GitLab pods are running
echo "1. Checking GitLab pods status..."
kubectl get pods -n gitlab -l app=webservice
echo ""
# Check hostAliases in webservice pods
echo "2. Verifying hostAliases in GitLab webservice..."
WEBSERVICE_POD=$(kubectl get pod -n gitlab -l app=webservice -o jsonpath='{.items[0].metadata.name}')
echo "Using pod: $WEBSERVICE_POD"
kubectl get pod -n gitlab "$WEBSERVICE_POD" -o jsonpath='{.spec.hostAliases}' | jq .
echo ""
# Test DNS resolution from inside GitLab pod
echo "3. Testing DNS resolution for auth.kube.huskypup.net from GitLab pod..."
kubectl exec -n gitlab "$WEBSERVICE_POD" -c webservice -- getent hosts auth.kube.huskypup.net || echo "getent not available, trying nslookup..."
echo ""
# Test HTTPS connectivity to Authentik
echo "4. Testing HTTPS connectivity to Authentik..."
kubectl exec -n gitlab "$WEBSERVICE_POD" -c webservice -- curl -I https://auth.kube.huskypup.net/.well-known/openid-configuration 2>&1 | head -20
echo ""
# Check OIDC configuration
echo "5. Checking OIDC discovery endpoint..."
kubectl exec -n gitlab "$WEBSERVICE_POD" -c webservice -- curl -s https://auth.kube.huskypup.net/application/o/gitlab/.well-known/openid-configuration 2>&1 | head -10
echo ""
# Check for any SSL errors in logs
echo "6. Checking recent GitLab logs for SSL errors..."
kubectl logs -n gitlab "$WEBSERVICE_POD" -c webservice --tail=50 | grep -i "ssl\|certificate\|openid" || echo "No SSL/certificate errors found in recent logs"
echo ""
echo "=== Verification Complete ==="
echo ""
echo "If you see 'HTTP/1.1 200 OK' or 'HTTP/2 200' above, OIDC is working correctly!"
echo "If you see SSL errors, check the hostAliases configuration."
+177
View File
@@ -0,0 +1,177 @@
#!/bin/bash
# Verify GitLab Redis password rotation automation is working
set -euo pipefail
echo "===================================================================="
echo "GitLab Redis Password Rotation Automation - Verification Script"
echo "===================================================================="
echo ""
# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
check_pass() {
echo -e "${GREEN}${NC} $1"
}
check_fail() {
echo -e "${RED}${NC} $1"
}
check_warn() {
echo -e "${YELLOW}${NC} $1"
}
ERRORS=0
# Check 1: CronJob exists
echo "Checking CronJob deployment..."
if kubectl get cronjob -n gitlab redis-secret-monitor >/dev/null 2>&1; then
check_pass "CronJob 'redis-secret-monitor' is deployed"
# Get schedule
SCHEDULE=$(kubectl get cronjob -n gitlab redis-secret-monitor -o jsonpath='{.spec.schedule}')
echo " Schedule: $SCHEDULE"
# Get last successful run
LAST_SUCCESS=$(kubectl get cronjob -n gitlab redis-secret-monitor -o jsonpath='{.status.lastSuccessfulTime}' 2>/dev/null || echo "Never")
echo " Last successful run: $LAST_SUCCESS"
else
check_fail "CronJob 'redis-secret-monitor' not found"
ERRORS=$((ERRORS + 1))
fi
echo ""
# Check 2: RBAC resources exist
echo "Checking RBAC resources..."
if kubectl get serviceaccount -n gitlab redis-restart-sa >/dev/null 2>&1; then
check_pass "ServiceAccount 'redis-restart-sa' exists"
else
check_fail "ServiceAccount 'redis-restart-sa' not found"
ERRORS=$((ERRORS + 1))
fi
if kubectl get role -n gitlab redis-restart-role >/dev/null 2>&1; then
check_pass "Role 'redis-restart-role' exists"
else
check_fail "Role 'redis-restart-role' not found"
ERRORS=$((ERRORS + 1))
fi
if kubectl get rolebinding -n gitlab redis-restart-binding >/dev/null 2>&1; then
check_pass "RoleBinding 'redis-restart-binding' exists"
else
check_fail "RoleBinding 'redis-restart-binding' not found"
ERRORS=$((ERRORS + 1))
fi
echo ""
# Check 3: StatefulSet has tracking annotation
echo "Checking StatefulSet annotations..."
SECRET_VERSION=$(kubectl get statefulset -n gitlab redis-gitlab -o jsonpath='{.spec.template.metadata.annotations.secret-version/redis-password}' 2>/dev/null || echo "")
if [ -n "$SECRET_VERSION" ]; then
check_pass "StatefulSet has tracking annotation"
echo " Tracked secret version: $SECRET_VERSION"
else
check_warn "StatefulSet missing tracking annotation (will be added on first CronJob run)"
fi
RESTART_TIME=$(kubectl get statefulset -n gitlab redis-gitlab -o jsonpath='{.spec.template.metadata.annotations.restarted-at}' 2>/dev/null || echo "")
if [ -n "$RESTART_TIME" ]; then
echo " Last restart: $RESTART_TIME"
fi
echo ""
# Check 4: Redis is running
echo "Checking Redis pod status..."
if kubectl get pod -n gitlab redis-gitlab-0 >/dev/null 2>&1; then
REDIS_STATUS=$(kubectl get pod -n gitlab redis-gitlab-0 -o jsonpath='{.status.phase}')
if [ "$REDIS_STATUS" = "Running" ]; then
check_pass "Redis pod is Running"
else
check_fail "Redis pod status: $REDIS_STATUS"
ERRORS=$((ERRORS + 1))
fi
else
check_fail "Redis pod 'redis-gitlab-0' not found"
ERRORS=$((ERRORS + 1))
fi
echo ""
# Check 5: Redis authentication works
echo "Testing Redis authentication..."
if RESULT=$(kubectl exec -n gitlab redis-gitlab-0 -- redis-cli -a "$(kubectl get secret -n gitlab redis-gitlab-secret -o jsonpath='{.data.password}' | base64 -d)" ping 2>&1 | grep PONG); then
check_pass "Redis authentication successful (PONG)"
else
check_fail "Redis authentication failed"
ERRORS=$((ERRORS + 1))
fi
echo ""
# Check 6: GitLab Sidekiq is healthy
echo "Checking GitLab Sidekiq status..."
SIDEKIQ_READY=$(kubectl get pods -n gitlab -l app=sidekiq -o jsonpath='{.items[*].status.containerStatuses[*].ready}' | grep -o "true" | wc -l)
SIDEKIQ_TOTAL=$(kubectl get pods -n gitlab -l app=sidekiq --no-headers | wc -l)
if [ "$SIDEKIQ_READY" -eq "$SIDEKIQ_TOTAL" ] && [ "$SIDEKIQ_TOTAL" -gt 0 ]; then
check_pass "Sidekiq pods are healthy ($SIDEKIQ_READY/$SIDEKIQ_TOTAL ready)"
else
check_warn "Sidekiq pods: $SIDEKIQ_READY/$SIDEKIQ_TOTAL ready"
fi
echo ""
# Check 7: Recent job executions
echo "Checking recent CronJob executions..."
JOB_COUNT=$(kubectl get jobs -n gitlab -l app=redis-secret-monitor --no-headers 2>/dev/null | wc -l)
if [ "$JOB_COUNT" -gt 0 ]; then
check_pass "Found $JOB_COUNT recent job execution(s)"
# Show last job
LAST_JOB=$(kubectl get jobs -n gitlab -l app=redis-secret-monitor --sort-by=.metadata.creationTimestamp -o name 2>/dev/null | tail -1)
if [ -n "$LAST_JOB" ]; then
echo ""
echo " Last job logs:"
kubectl logs -n gitlab "$LAST_JOB" 2>/dev/null | sed 's/^/ /'
fi
else
check_warn "No recent job executions (CronJob may not have run yet)"
fi
echo ""
# Check 8: Secret version comparison
echo "Checking secret version synchronization..."
CURRENT_SECRET_VERSION=$(kubectl get secret -n gitlab redis-gitlab-secret -o jsonpath='{.metadata.resourceVersion}')
TRACKED_SECRET_VERSION=$(kubectl get statefulset -n gitlab redis-gitlab -o jsonpath='{.spec.template.metadata.annotations.secret-version/redis-password}' 2>/dev/null || echo "")
echo " Current secret version: $CURRENT_SECRET_VERSION"
echo " Tracked version in StatefulSet: $TRACKED_SECRET_VERSION"
if [ "$CURRENT_SECRET_VERSION" = "$TRACKED_SECRET_VERSION" ]; then
check_pass "Secret versions are synchronized"
elif [ -z "$TRACKED_SECRET_VERSION" ]; then
check_warn "StatefulSet not yet tracking secret version (first sync pending)"
else
check_warn "Secret versions differ - restart pending on next CronJob run"
echo " Next scheduled run will synchronize versions"
fi
echo ""
# Summary
echo "===================================================================="
if [ $ERRORS -eq 0 ]; then
echo -e "${GREEN}✓ All checks passed!${NC}"
echo ""
echo "Redis password rotation automation is properly configured."
echo "The system will automatically restart Redis when passwords rotate."
else
echo -e "${RED}$ERRORS check(s) failed${NC}"
echo ""
echo "Please review the errors above and ensure all automation"
echo "components are properly deployed."
exit 1
fi
echo "===================================================================="