Initial commit.

This commit is contained in:
2025-04-27 14:57:00 -07:00
commit 84376fb3d5
63 changed files with 5645 additions and 0 deletions

3
bin/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Sovereign Cloud Binaries
These are the scripts that help you manage your cloud.

69
bin/chart-diff Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/bash
# chart-diff
# Shows differences between current and new chart version using gomplate for values
# Usage: chart-diff CHART [RELEASE_NAME] [NAMESPACE]
set -e
# Get script directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# Load environment variables if needed
if [[ -z "$ENVIRONMENT" ]]; then
if [[ -f "${REPO_ROOT}/load-env.sh" ]]; then
source "${REPO_ROOT}/load-env.sh"
fi
fi
# Print usage
if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
echo "Usage: $(basename $0) CHART [RELEASE_NAME] [NAMESPACE]"
echo ""
echo "Shows differences between current and new chart version."
echo ""
echo " CHART Chart name in charts/ directory"
echo " RELEASE_NAME Release name (defaults to CHART)"
echo " NAMESPACE Namespace (defaults to RELEASE_NAME)"
echo ""
echo ""
exit 1
fi
CHART="$1"
RELEASE_NAME="${2:-$CHART}"
NAMESPACE="${3:-$RELEASE_NAME}"
# We use kubectl diff now, no need for helm-diff plugin
# Check if chart exists
CHART_PATH="${REPO_ROOT}/charts/${CHART}"
if [[ ! -d "$CHART_PATH" ]]; then
echo "Error: Chart not found at ${CHART_PATH}"
exit 1
fi
# We'll use chart-template for values, so we don't need to check here
# Show what would change
echo "==> Showing differences for chart: $CHART"
echo "==> Release name: $RELEASE_NAME"
echo "==> Namespace: $NAMESPACE"
echo ""
# Create temporary files for the template output
TEMP_OUTPUT=$(mktemp)
CLEAN_OUTPUT=$(mktemp)
# Generate the template and filter out the header text
"${SCRIPT_DIR}/chart-template" "$CHART" "$RELEASE_NAME" "$NAMESPACE" > "$TEMP_OUTPUT"
sed -n '/^---$/,$p' "$TEMP_OUTPUT" > "$CLEAN_OUTPUT"
# Use kubectl diff to show actual differences between current state and template
echo "==> Showing differences between current cluster state and template for $CHART:"
echo "==> (+ indicates additions, - indicates removals)"
echo ""
kubectl diff -f "$CLEAN_OUTPUT" || true
# Clean up
rm "$TEMP_OUTPUT" "$CLEAN_OUTPUT"

108
bin/chart-install Executable file
View File

@@ -0,0 +1,108 @@
#!/bin/bash
# chart-install
# Installs a Helm chart using gomplate for templating values
# Usage: chart-install CHART [RELEASE_NAME] [NAMESPACE] [--dry-run]
set -e
# Get script directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# Load environment variables if needed
if [[ -z "$ENVIRONMENT" ]]; then
if [[ -f "${REPO_ROOT}/load-env.sh" ]]; then
source "${REPO_ROOT}/load-env.sh"
fi
fi
# Print usage
if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
echo "Usage: $(basename $0) CHART [RELEASE_NAME] [NAMESPACE] [--dry-run]"
echo ""
echo "Install or upgrade a chart with environment variable substitution."
echo ""
echo " CHART Chart name in charts/ directory"
echo " RELEASE_NAME Release name (defaults to CHART)"
echo " NAMESPACE Namespace (defaults to RELEASE_NAME)"
echo ""
echo "Options:"
echo " --dry-run Show what would be installed without actually installing"
echo ""
exit 1
fi
# Check for dry run flag
DRY_RUN=false
for arg in "$@"; do
if [ "$arg" == "--dry-run" ]; then
DRY_RUN=true
fi
done
CHART="$1"
RELEASE_NAME="${2:-$CHART}"
NAMESPACE="${3:-$RELEASE_NAME}"
# Check if chart exists
CHART_PATH="${REPO_ROOT}/charts/${CHART}"
if [[ ! -d "$CHART_PATH" ]]; then
echo "Error: Chart not found at ${CHART_PATH}"
exit 1
fi
# Update chart dependencies if Chart.yaml has dependencies section
if [ -f "${CHART_PATH}/Chart.yaml" ] && grep -q "dependencies:" "${CHART_PATH}/Chart.yaml"; then
echo "Updating dependencies for chart: $CHART"
helm dependency update "$CHART_PATH"
fi
# We'll use chart-template for values, so we don't need to check here
# Create namespace (unless --dry-run was specified)
if [ "$DRY_RUN" == "false" ]; then
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
fi
# Run the helm command
echo "Installing chart: $CHART as $RELEASE_NAME in namespace $NAMESPACE"
if [ "$DRY_RUN" == "true" ]; then
echo "==> DRY RUN: Would install the following resources:"
"${SCRIPT_DIR}/chart-template" "$CHART" "$RELEASE_NAME" "$NAMESPACE"
exit 0
fi
# For actual installation, create a temporary file to capture template output
TEMP_OUTPUT=$(mktemp)
"${SCRIPT_DIR}/chart-template" "$CHART" "$RELEASE_NAME" "$NAMESPACE" > "$TEMP_OUTPUT"
# Apply the template to the cluster
kubectl apply -f "$TEMP_OUTPUT"
# Clean up
rm "$TEMP_OUTPUT"
# Print helpful information (if not dry run)
if [ "$DRY_RUN" == "false" ]; then
echo ""
echo "✅ Successfully installed/upgraded $RELEASE_NAME in namespace $NAMESPACE"
echo ""
echo "To check the status:"
echo " kubectl get all -n $NAMESPACE -l app.kubernetes.io/instance=$RELEASE_NAME"
echo ""
fi
# Check for post-install instructions (only if not dry run)
if [ "$DRY_RUN" == "false" ]; then
INSTRUCTIONS_FILE="${CHART_PATH}/POST_INSTALL_NOTES.txt"
if [[ -f "$INSTRUCTIONS_FILE" ]]; then
# Process environment variables in instructions (using the same environment)
echo ""
echo "Post-installation instructions:"
echo "==============================="
gomplate -f "$INSTRUCTIONS_FILE"
else
echo "For more information, see the documentation in ${CHART_PATH}/README.md"
fi
fi

65
bin/chart-template Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/bash
# chart-template
# Renders the template for a Helm chart using gomplate for values files
# Usage: chart-template CHART [RELEASE_NAME] [NAMESPACE]
set -e
# Get script directories
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
# Load environment variables if needed
if [[ -f "${REPO_ROOT}/load-env.sh" ]]; then
source "${REPO_ROOT}/load-env.sh"
fi
# Print usage
if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
echo "Usage: $(basename $0) CHART [RELEASE_NAME] [NAMESPACE]"
echo ""
echo "Renders the Kubernetes templates for a chart with environment variable substitution."
echo ""
echo " CHART Chart name in charts/ directory"
echo " RELEASE_NAME Release name (defaults to CHART)"
echo " NAMESPACE Namespace (defaults to RELEASE_NAME)"
echo ""
exit 1
fi
CHART="$1"
RELEASE_NAME="${2:-$CHART}"
NAMESPACE="${3:-$RELEASE_NAME}"
# Check if chart exists
CHART_PATH="${REPO_ROOT}/charts/${CHART}"
if [[ ! -d "$CHART_PATH" ]]; then
echo "Error: Chart not found at ${CHART_PATH}"
exit 1
fi
# Check if template values file exists
TPL_VALUES_FILE="${CHART_PATH}/values.template.yaml"
if [[ ! -f "$TPL_VALUES_FILE" ]]; then
echo "Error: Template values file not found at ${TPL_VALUES_FILE}"
exit 1
fi
# Set variables needed for template
export RELEASE_NAME="$RELEASE_NAME"
export NAMESPACE="$NAMESPACE"
export CHART="$CHART"
# No headers - just let helm template output the YAML
# Create temporary values file with gomplate
TEMP_VALUES=$(mktemp)
gomplate -f "$TPL_VALUES_FILE" > "$TEMP_VALUES"
# Run helm template
helm template "$RELEASE_NAME" "$CHART_PATH" \
--namespace "$NAMESPACE" \
--values "$TEMP_VALUES"
# Clean up
rm "$TEMP_VALUES"

30
bin/dashboard-token Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
# Set up colors for better readability
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NC='\033[0m' # No Color
# The namespace where the dashboard is installed
NAMESPACE=kubernetes-dashboard
# Get the token
TOKEN=$(kubectl -n $NAMESPACE get secret dashboard-admin-token -o jsonpath="{.data.token}" | base64 -d)
# Print instructions
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ ${GREEN}Kubernetes Dashboard Token${BLUE} ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
echo
echo -e "${GREEN}Use this token to authenticate to the Kubernetes Dashboard:${NC}"
echo
echo -e "${PURPLE}$TOKEN${NC}"
echo
# Save token to clipboard if xclip is available
if command -v xclip &> /dev/null; then
echo -n "$TOKEN" | xclip -selection clipboard
echo -e "${GREEN}Token has been copied to your clipboard!${NC}"
fi

137
bin/deploy-service Executable file
View File

@@ -0,0 +1,137 @@
#!/bin/bash
set -e
# Default values
SERVICE_NAME=""
DRY_RUN=false
# Source environment variables from load-env.sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
if [ -f "$REPO_DIR/load-env.sh" ]; then
source "$REPO_DIR/load-env.sh"
fi
function show_help {
echo "Usage: $0 SERVICE_NAME [options]"
echo ""
echo "Arguments:"
echo " SERVICE_NAME Name of the service to deploy (directory name in services/)"
echo ""
echo "Optional arguments:"
echo " --dry-run Preview the processed configuration without applying"
echo " --help Show this help message"
echo ""
echo "Examples:"
echo " $0 example-app"
echo " $0 blog --dry-run"
exit 1
}
# Legacy mode check for type-based commands
if [[ "$1" == "--type" ]]; then
echo "Warning: Using legacy mode (generate and deploy in one step)"
echo "Consider using generate-service followed by deploy-service instead."
echo "Continuing with legacy mode..."
echo ""
# Capture all arguments
ALL_ARGS="$@"
# Extract service name from arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--name)
SERVICE_NAME_LEGACY="$2"
break
;;
*)
shift
;;
esac
done
# Generate the service configuration first
TMP_DIR=$(mktemp -d)
TMP_FILE="$TMP_DIR/service.yaml"
$SCRIPT_DIR/generate-service $ALL_ARGS --output "$TMP_DIR"
# Now deploy it using the service name
if [[ -n "$SERVICE_NAME_LEGACY" ]]; then
exec $0 "$SERVICE_NAME_LEGACY"
else
echo "Error: Legacy mode requires --name parameter"
exit 1
fi
exit $?
fi
# Parse command-line arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--dry-run)
DRY_RUN=true
shift
;;
--help)
show_help
;;
-*)
echo "Unknown option: $1"
show_help
;;
*)
# First non-option argument is the service name
SERVICE_NAME="$1"
shift
;;
esac
done
# Validate service name
if [[ -z "$SERVICE_NAME" ]]; then
echo "Error: SERVICE_NAME must be provided"
show_help
fi
# Construct the service file path
SERVICE_FILE="$REPO_DIR/services/$SERVICE_NAME/service.yaml"
if [[ ! -f "$SERVICE_FILE" ]]; then
echo "Error: Service file not found for $SERVICE_NAME at $SERVICE_FILE"
exit 1
fi
# Create temporary file for the processed manifest
TEMP_FILE=$(mktemp)
# Ensure DOMAIN is exported for template substitution
export DOMAIN="$DOMAIN"
# Process the service file with variable substitution
echo "Processing service file: $SERVICE_FILE"
cat "$SERVICE_FILE" | envsubst > "$TEMP_FILE"
# Handle dry run mode
if [[ "$DRY_RUN" == "true" ]]; then
cat "$TEMP_FILE"
rm "$TEMP_FILE"
exit 0
fi
# Extract namespace from the processed file (for creating it if needed)
NAMESPACE=$(grep -o "namespace: [a-zA-Z0-9_-]\+" "$TEMP_FILE" | head -1 | cut -d' ' -f2)
if [[ -n "$NAMESPACE" ]]; then
# Create the namespace if it doesn't exist (using kubectl create which is idempotent with --dry-run=client)
echo "Creating namespace $NAMESPACE if it doesn't exist..."
kubectl create namespace "$NAMESPACE" --dry-run=client | kubectl create -f - 2>/dev/null || true
fi
# Apply the service
echo "Applying service configuration..."
kubectl apply -f "$TEMP_FILE"
rm "$TEMP_FILE"
echo "✅ Service deployed successfully!"

186
bin/generate-service Executable file
View File

@@ -0,0 +1,186 @@
#!/bin/bash
set -e
# Source environment variables for defaults and domain settings
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$SCRIPT_DIR/../load-env.sh" ]; then
source "$SCRIPT_DIR/../load-env.sh"
fi
# Default values
SERVICE_TYPE=""
SERVICE_NAME=""
NAMESPACE=""
IMAGE=""
PORT=""
SERVICE_DOMAIN=""
OUTPUT_DIR=""
function show_help {
echo "Usage: $0 --type [public|internal|database|microservice] --name SERVICE_NAME [options]"
echo ""
echo "Required arguments:"
echo " --type TYPE Service type (public, internal, database, or microservice)"
echo " --name NAME Service name"
echo ""
echo "Optional arguments:"
echo " --namespace NAMESPACE Kubernetes namespace (defaults to service name)"
echo " --image IMAGE Container image (defaults to nginx:latest for most types)"
echo " --port PORT Container port (defaults to 80)"
echo " --domain DOMAIN Custom domain (defaults to TYPE-specific domain)"
echo " --output DIR Output directory (defaults to services/NAME)"
echo " --help Show this help message"
echo ""
echo "Examples:"
echo " $0 --type public --name blog"
echo " $0 --type internal --name admin --image my-admin:v1 --port 8080"
echo " $0 --type database --name mysql --image mysql:8.0 --port 3306"
echo " $0 --type microservice --name auth --image auth-service:v1 --port 9000"
exit 1
}
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
--type)
SERVICE_TYPE="$2"
shift 2
;;
--name)
SERVICE_NAME="$2"
shift 2
;;
--namespace)
NAMESPACE="$2"
shift 2
;;
--image)
IMAGE="$2"
shift 2
;;
--port)
PORT="$2"
shift 2
;;
--domain)
SERVICE_DOMAIN="$2"
shift 2
;;
--output)
OUTPUT_DIR="$2"
shift 2
;;
--help)
show_help
;;
*)
echo "Unknown option: $1"
show_help
;;
esac
done
# Validate required parameters
if [[ -z "$SERVICE_TYPE" ]]; then
echo "Error: Service type is required"
show_help
fi
if [[ -z "$SERVICE_NAME" ]]; then
echo "Error: Service name is required"
show_help
fi
# Validate service type
if [[ "$SERVICE_TYPE" != "public" && "$SERVICE_TYPE" != "internal" && "$SERVICE_TYPE" != "database" && "$SERVICE_TYPE" != "microservice" ]]; then
echo "Error: Invalid service type. Must be public, internal, database, or microservice."
show_help
fi
# Set defaults
if [[ -z "$NAMESPACE" ]]; then
NAMESPACE="$SERVICE_NAME"
fi
if [[ -z "$IMAGE" ]]; then
if [[ "$SERVICE_TYPE" == "database" ]]; then
IMAGE="mariadb:10.6"
else
IMAGE="nginx:latest"
fi
fi
if [[ -z "$PORT" ]]; then
if [[ "$SERVICE_TYPE" == "database" ]]; then
PORT="3306"
else
PORT="80"
fi
fi
if [[ -z "$SERVICE_DOMAIN" ]]; then
if [[ "$SERVICE_TYPE" == "public" ]]; then
SERVICE_DOMAIN="\${SERVICE_NAME}.\${DOMAIN}"
elif [[ "$SERVICE_TYPE" == "internal" ]]; then
SERVICE_DOMAIN="\${SERVICE_NAME}.internal.\${DOMAIN}"
elif [[ "$SERVICE_TYPE" == "microservice" ]]; then
SERVICE_DOMAIN="\${SERVICE_NAME}.svc.\${DOMAIN}"
else
SERVICE_DOMAIN="\${SERVICE_NAME}.db.\${DOMAIN}"
fi
fi
# Set default output directory if not provided
if [[ -z "$OUTPUT_DIR" ]]; then
OUTPUT_DIR="$SCRIPT_DIR/../services/$SERVICE_NAME"
fi
echo "Generating $SERVICE_TYPE service configuration for: $SERVICE_NAME"
echo "Namespace: $NAMESPACE"
echo "Image: $IMAGE"
echo "Port: $PORT"
echo "Domain Template: $SERVICE_DOMAIN"
echo "Output Directory: $OUTPUT_DIR"
echo
# Get the appropriate template
if [[ "$SERVICE_TYPE" == "microservice" ]]; then
TEMPLATE_FILE="$SCRIPT_DIR/../services/templates/microservice/service.yaml"
else
TEMPLATE_FILE="$SCRIPT_DIR/../services/templates/${SERVICE_TYPE}-service/service.yaml"
fi
if [[ ! -f "$TEMPLATE_FILE" ]]; then
echo "Error: Template file not found: $TEMPLATE_FILE"
exit 1
fi
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
# Create the service YAML
echo "Creating service configuration..."
# Prepare variables for substitution
export SERVICE_NAME="$SERVICE_NAME"
export SERVICE_NAMESPACE="$NAMESPACE"
export SERVICE_IMAGE="\"$IMAGE\""
export SERVICE_PORT="$PORT"
export SERVICE_DOMAIN="$SERVICE_DOMAIN"
# Process the template with variable substitution
mkdir -p "$OUTPUT_DIR"
# Define which variables to replace - only those from command arguments
VARS_TO_REPLACE='${SERVICE_NAME},${SERVICE_NAMESPACE},${SERVICE_IMAGE},${SERVICE_PORT},${SERVICE_DOMAIN}'
# Process the template, only substituting the variables from arguments
cat "$TEMPLATE_FILE" | envsubst "$VARS_TO_REPLACE" > "$OUTPUT_DIR/service.yaml"
echo "✅ Service configuration generated successfully!"
echo "Configuration file: $OUTPUT_DIR/service.yaml"
echo ""
echo "To deploy this service configuration:"
echo " ./bin/deploy-service $SERVICE_NAME"
echo ""
echo "To customize further, edit the generated file before deployment."

67
bin/install-ca-ubuntu Executable file
View File

@@ -0,0 +1,67 @@
#!/bin/bash
# This script installs the local CA certificate on Ubuntu systems to avoid
# certificate warnings in browsers when accessing internal cloud services.
# Set up error handling
set -e
# Define colors for better readability
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
CA_DIR="/home/payne/repos/cloud.payne.io-setup/ca"
CA_FILE="$CA_DIR/ca.crt"
TARGET_DIR="/usr/local/share/ca-certificates"
TARGET_FILE="cloud-payne-local-ca.crt"
echo -e "${BLUE}=== Installing Local CA Certificate on Ubuntu ===${NC}"
echo
# Check if CA file exists
if [ ! -f "$CA_FILE" ]; then
echo -e "${RED}CA certificate not found at $CA_FILE${NC}"
echo -e "${YELLOW}Please run the create-local-ca script first:${NC}"
echo -e "${BLUE}./bin/create-local-ca${NC}"
exit 1
fi
# Copy to the system certificate directory
echo -e "${YELLOW}Copying CA certificate to $TARGET_DIR/$TARGET_FILE...${NC}"
sudo cp "$CA_FILE" "$TARGET_DIR/$TARGET_FILE"
# Update the CA certificates
echo -e "${YELLOW}Updating system CA certificates...${NC}"
sudo update-ca-certificates
# Update browsers' CA store (optional, for Firefox)
if [ -d "$HOME/.mozilla" ]; then
echo -e "${YELLOW}You may need to manually import the certificate in Firefox:${NC}"
echo -e "1. Open Firefox"
echo -e "2. Go to Preferences > Privacy & Security > Certificates"
echo -e "3. Click 'View Certificates' > 'Authorities' tab"
echo -e "4. Click 'Import' and select $CA_FILE"
echo -e "5. Check 'Trust this CA to identify websites' and click OK"
fi
# Check popular browsers
if command -v google-chrome &> /dev/null; then
echo -e "${YELLOW}For Chrome, the system-wide certificate should now be recognized${NC}"
echo -e "${YELLOW}You may need to restart the browser${NC}"
fi
echo
echo -e "${GREEN}=== CA Certificate Installation Complete ===${NC}"
echo
echo -e "${YELLOW}System-wide CA certificate has been installed.${NC}"
echo -e "${YELLOW}You should now be able to access the Kubernetes Dashboard without certificate warnings:${NC}"
echo -e "${BLUE}https://kubernetes-dashboard.in.cloud.payne.io${NC}"
echo
echo -e "${YELLOW}If you still see certificate warnings, try:${NC}"
echo "1. Restart your browser"
echo "2. Clear your browser's cache and cookies"
echo "3. If using a non-standard browser, you may need to import the certificate manually"
echo

211
bin/setup-systemd-resolved-dns Executable file
View File

@@ -0,0 +1,211 @@
#!/bin/bash
# Set up error handling
set -e
# Define colors for better readability
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Load environment variables
if [ -f "$(dirname "$0")/../load-env.sh" ]; then
echo -e "${YELLOW}Loading environment variables...${NC}"
source "$(dirname "$0")/../load-env.sh"
fi
# Get cluster IP
echo -e "${YELLOW}Getting cluster IP address...${NC}"
CLUSTER_IP=$(kubectl get -n kube-system service traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
if [ -z "$CLUSTER_IP" ]; then
echo -e "${RED}Failed to get cluster IP. Is Traefik running?${NC}"
exit 1
fi
echo -e "${YELLOW}Using cluster IP: ${CLUSTER_IP}${NC}"
# Domain settings
DOMAIN="cloud.payne.io"
INTERNAL_DOMAIN="in.${DOMAIN}"
DASHBOARD_DOMAIN="kubernetes-dashboard.${INTERNAL_DOMAIN}"
echo -e "${BLUE}=== Setting up Split DNS with systemd-resolved ===${NC}"
echo -e "${YELLOW}Internal Domain: ${INTERNAL_DOMAIN}${NC}"
echo -e "${YELLOW}Dashboard Domain: ${DASHBOARD_DOMAIN}${NC}"
echo
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}This script must be run as root to configure systemd-resolved.${NC}"
echo -e "${YELLOW}Please run: sudo $0${NC}"
exit 1
fi
# Create systemd-resolved configuration directory if it doesn't exist
echo -e "${YELLOW}Creating systemd-resolved configuration directory...${NC}"
mkdir -p /etc/systemd/resolved.conf.d/
# Create the configuration file for split DNS
echo -e "${YELLOW}Creating split DNS configuration...${NC}"
cat > /etc/systemd/resolved.conf.d/split-dns.conf << EOF
[Resolve]
# Use Google DNS servers as fallback
FallbackDNS=8.8.8.8 8.8.4.4
# Define our domain for special handling
Domains=~${INTERNAL_DOMAIN}
# Enable split DNS
DNSStubListenerExtra=${CLUSTER_IP}
EOF
# Create a static host entry for the dashboard domain
echo -e "${YELLOW}Creating static address mapping for ${DASHBOARD_DOMAIN}...${NC}"
mkdir -p /etc/systemd/resolved.conf.d/
cat > /etc/systemd/resolved.conf.d/static-domains.conf << EOF
[Resolve]
# Map our dashboard domain to the cluster IP
$(echo "${DASHBOARD_DOMAIN} ${CLUSTER_IP}" | awk '{print "DNS=" $2 "#" $1}')
EOF
# Restart systemd-resolved
echo -e "${YELLOW}Restarting systemd-resolved...${NC}"
systemctl restart systemd-resolved
# Remove immutable flag from resolv.conf if set
if lsattr /etc/resolv.conf 2>/dev/null | grep -q 'i'; then
echo -e "${YELLOW}Removing immutable flag from /etc/resolv.conf...${NC}"
chattr -i /etc/resolv.conf
fi
# Configure resolv.conf to use systemd-resolved
echo -e "${YELLOW}Configuring /etc/resolv.conf to use systemd-resolved...${NC}"
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
# Now for the Kubernetes parts
echo -e "${YELLOW}Setting up Kubernetes components...${NC}"
# Get email for Let's Encrypt
echo -e "${YELLOW}Please enter an email address for Let's Encrypt registration:${NC}"
read -p "Email: " EMAIL_ADDRESS
# Ensure cert-manager namespace exists
kubectl get namespace cert-manager >/dev/null 2>&1 || kubectl create namespace cert-manager
# Install cert-manager if needed
if ! kubectl get deployment -n cert-manager cert-manager >/dev/null 2>&1; then
echo -e "${YELLOW}Installing cert-manager...${NC}"
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--set installCRDs=true
# Wait for cert-manager to be ready
echo -e "${YELLOW}Waiting for cert-manager to be ready (this may take a minute)...${NC}"
sleep 30
kubectl wait --for=condition=available --timeout=300s deployment/cert-manager -n cert-manager
kubectl wait --for=condition=available --timeout=300s deployment/cert-manager-webhook -n cert-manager
fi
# Ensure kubernetes-dashboard namespace exists
kubectl get namespace kubernetes-dashboard >/dev/null 2>&1 || kubectl create namespace kubernetes-dashboard
# Install the dashboard if not already installed
if ! kubectl get deployment -n kubernetes-dashboard kubernetes-dashboard >/dev/null 2>&1; then
echo -e "${YELLOW}Installing Kubernetes Dashboard...${NC}"
"$(dirname "$0")/install-simple-dashboard"
fi
# Create a ClusterIssuer for Let's Encrypt
echo -e "${YELLOW}Setting up Let's Encrypt ClusterIssuer...${NC}"
cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ${EMAIL_ADDRESS}
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
EOF
# Check dashboard service name and port
echo -e "${YELLOW}Checking kubernetes-dashboard service...${NC}"
DASHBOARD_SERVICE=$(kubectl get svc -n kubernetes-dashboard -o name | grep kubernetes-dashboard | grep -v metrics-scraper | grep -v api | grep -v auth | head -1)
if [ -z "$DASHBOARD_SERVICE" ]; then
echo -e "${RED}Kubernetes Dashboard service not found. Please check your installation.${NC}"
exit 1
fi
# Get the service name without the "service/" prefix
DASHBOARD_SERVICE_NAME=$(echo $DASHBOARD_SERVICE | cut -d'/' -f2)
echo -e "${YELLOW}Found dashboard service: ${DASHBOARD_SERVICE_NAME}${NC}"
# Get the service port
DASHBOARD_PORT=$(kubectl get svc $DASHBOARD_SERVICE_NAME -n kubernetes-dashboard -o jsonpath='{.spec.ports[0].port}')
echo -e "${YELLOW}Dashboard port: ${DASHBOARD_PORT}${NC}"
# Create an Ingress with TLS
echo -e "${YELLOW}Creating ingress with TLS for ${DASHBOARD_DOMAIN}...${NC}"
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kubernetes-dashboard
namespace: kubernetes-dashboard
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/service.serversscheme: https
traefik.ingress.kubernetes.io/service.serverstransport.insecureskipverify: "true"
spec:
tls:
- hosts:
- ${DASHBOARD_DOMAIN}
secretName: kubernetes-dashboard-tls
rules:
- host: ${DASHBOARD_DOMAIN}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ${DASHBOARD_SERVICE_NAME}
port:
number: ${DASHBOARD_PORT}
EOF
echo
echo -e "${GREEN}=== Split DNS Setup Complete! ===${NC}"
echo
echo -e "${YELLOW}Your Kubernetes Dashboard will be available at:${NC}"
echo -e "${BLUE}https://${DASHBOARD_DOMAIN}${NC}"
echo
echo -e "${YELLOW}Key points:${NC}"
echo "1. systemd-resolved is now configured to resolve ${INTERNAL_DOMAIN} domains locally"
echo "2. The dashboard domain ${DASHBOARD_DOMAIN} is mapped to ${CLUSTER_IP}"
echo "3. Let's Encrypt will issue a valid certificate for secure HTTPS (may take a few minutes)"
echo "4. External users cannot access these domains (special DNS configuration required)"
echo
echo -e "${YELLOW}To test the DNS resolution:${NC}"
echo -e "${BLUE}nslookup ${DASHBOARD_DOMAIN}${NC}"
echo
echo -e "${YELLOW}To verify systemd-resolved configuration:${NC}"
echo -e "${BLUE}resolvectl status${NC}"
echo
echo -e "${YELLOW}Certificate status:${NC}"
echo -e "${BLUE}kubectl get certificate -n kubernetes-dashboard${NC}"
echo