diff --git a/common.nix b/common.nix index 3d9647d..907f43f 100644 --- a/common.nix +++ b/common.nix @@ -321,6 +321,7 @@ isoConfig // { dnsutils dig gnutar + openssl # Add this for certificate generation ]; # System Setup Service (Root Tasks) @@ -443,25 +444,6 @@ isoConfig // { ]; }; }; - - # System Packages - environment.systemPackages = with pkgs; [ - openssh - curl - git - networkmanager - docker - docker-compose - bash - wget - jq - tree - nano - dnsutils - dig - gnutar - openssl # Add this for certificate generation - ]; # Enhanced Bash Configuration with All Features programs.bash.interactiveShellInit = @@ -520,105 +502,482 @@ isoConfig // { complete -F _workshop_completion deploy browser connect abra # Core Workshop Functions - setup() { - echo "๐Ÿ”ง Setting up LOCAL Co-op Cloud environment..." - - # Verify DNS first + setup() { + echo "๐Ÿ”ง Setting up LOCAL Co-op Cloud environment..." + + # Run permission checks first + setup_permissions || return 1 + + # Run setup steps individually + setup_dns || return 1 + setup_docker || return 1 + setup_abra_server || return 1 + setup_certificates || return 1 + setup_traefik || return 1 + + echo "๐ŸŽ‰ Setup complete!" + } + + setup_permissions() { + echo "๐Ÿ” Checking system permissions and prerequisites..." + + # Check if running as workshop user + if [[ "$(whoami)" != "workshop" ]]; then + echo "โš ๏ธ Not running as workshop user (current: $(whoami))" + echo " This may cause permission issues. Consider running as workshop user." + else + echo "โœ… Running as workshop user" + fi + + # Check sudo access + if sudo -n true 2>/dev/null; then + echo "โœ… Sudo access available (no password required)" + else + echo "โš ๏ธ Sudo may require password - this could interrupt automated setup" + fi + + # Check Docker group membership + if id -nG | grep -q "docker"; then + echo "โœ… User is in docker group" + else + echo "โš ๏ธ User not in docker group - Docker commands may fail" + echo " Current groups: $(id -nG)" + fi + + # Check if abra is available + if command -v abra >/dev/null 2>&1; then + echo "โœ… abra command found: $(which abra)" + else + echo "โŒ abra command not found in PATH" + echo " PATH: $PATH" + return 1 + fi + + # Check abra server configuration + if sudo abra server ls 2>/dev/null | grep -q "default"; then + echo "โœ… Abra default server configured" + else + echo "โš ๏ธ Abra default server not configured - will be set up" + fi + + # Check /tmp permissions + if [[ -w "/tmp" ]]; then + echo "โœ… /tmp directory is writable" + else + echo "โŒ /tmp directory is not writable" + ls -ld /tmp + return 1 + fi + + # Check openssl availability + if command -v openssl >/dev/null 2>&1; then + echo "โœ… OpenSSL available: $(openssl version | head -1)" + else + echo "โŒ OpenSSL not found - certificate generation will fail" + return 1 + fi + + # Check curl availability + if command -v curl >/dev/null 2>&1; then + echo "โœ… curl available for health checks" + else + echo "โš ๏ธ curl not found - health checks may not work properly" + fi + + echo "๐ŸŽฏ Permission checks complete!" + } + + setup_dns() { + echo "๐ŸŒ Step 1: Verifying DNS configuration..." + if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then - echo "๐Ÿ”„ Restarting DNS..." + echo "๐Ÿ”„ DNS not working, restarting dnsmasq..." sudo systemctl restart dnsmasq sleep 3 + + # Test again + if nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then + echo "โœ… DNS restarted successfully" + else + echo "โŒ DNS restart failed" + return 1 + fi + else + echo "โœ… DNS working correctly" fi - - # Ensure Docker Swarm + proxy network + } + + setup_docker() { + echo "๐Ÿณ Step 2: Setting up Docker Swarm and networks..." + + # Check Docker status if ! docker info 2>/dev/null | grep -q "Swarm: active"; then echo "๐Ÿ”ฅ Initializing Docker Swarm..." - docker swarm init --advertise-addr 127.0.0.1 + if docker swarm init --advertise-addr 127.0.0.1; then + echo "โœ… Docker Swarm initialized" + else + echo "โŒ Docker Swarm initialization failed" + return 1 + fi + else + echo "โœ… Docker Swarm already active" fi - + + # Check proxy network if ! docker network ls | grep -q "proxy"; then echo "๐ŸŒ Creating proxy network..." - docker network create -d overlay proxy + if docker network create -d overlay proxy; then + echo "โœ… Proxy network created" + else + echo "โŒ Proxy network creation failed" + return 1 + fi + else + echo "โœ… Proxy network exists" fi - - # Add LOCAL server (critical difference!) + } + + setup_abra_server() { + echo "๐Ÿ—„๏ธ Step 3: Setting up Abra server..." + if ! sudo abra server ls 2>/dev/null | grep -q "default"; then - echo "๐Ÿ—„๏ธ Adding LOCAL server..." - sudo abra server add --local - echo "โœ… Local server registered" + echo "๐Ÿ—„๏ธ Adding LOCAL server to abra..." + if sudo abra server add --local; then + echo "โœ… Local server registered" + else + echo "โŒ Failed to add local server" + return 1 + fi + else + echo "โœ… Abra server already configured" fi - - # Create self-signed certificate for offline use - echo "๐Ÿ” Setting up self-signed certificates for offline use..." - - # Create temporary cert directory - CERT_DIR="/tmp/workshop-certs" - mkdir -p $CERT_DIR - - # Generate self-signed certificate for *.workshop.local - if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then - openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout "$CERT_DIR/workshop.key" \ - -out "$CERT_DIR/workshop.crt" \ - -subj "/CN=*.workshop.local" \ - -config <(printf "[req]\ndistinguished_name=req\n[v3_req]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\n") \ - -extensions v3_req 2>/dev/null || true - echo "๐Ÿ”‘ Generated self-signed certificate" - fi - - # Create and configure Traefik for OFFLINE mode - if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then - echo "๐Ÿš€ Creating Traefik app for OFFLINE use..." - sudo abra app new traefik --domain=traefik.workshop.local --server=default - - # Configure traefik for offline/local development - TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env" - - echo "โš™๏ธ Configuring Traefik for offline mode..." - # Create offline-friendly traefik configuration - sudo tee -a "$TRAEFIK_ENV" >/dev/null </dev/null || echo "unknown") + echo " Directory permissions: $dir_perms" + echo " Directory owner: $(stat -c "%U:%G" "$CERT_DIR" 2>/dev/null || echo "unknown")" + } + + setup_certificates_generate() { + echo "๐Ÿ”‘ Generating self-signed certificate..." + + CERT_FILE="$CERT_DIR/workshop.crt" + KEY_FILE="$CERT_DIR/workshop.key" + + echo " Certificate file: $CERT_FILE" + echo " Key file: $KEY_FILE" + + # Check if openssl is available + if ! command -v openssl >/dev/null 2>&1; then + echo "โŒ OpenSSL not found in PATH" + which openssl || echo " openssl command not found" + return 1 + fi + + echo " OpenSSL version: $(openssl version)" + + # Check if certificate already exists + if [[ -f "$CERT_FILE" ]]; then + echo " โš ๏ธ Certificate file already exists, removing..." + rm -f "$CERT_FILE" "$KEY_FILE" || { + echo "โŒ Failed to remove existing certificate files" + return 1 + } + fi + + # Generate certificate with detailed output + echo " Generating RSA key and certificate..." + if openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout "$KEY_FILE" \ + -out "$CERT_FILE" \ + -subj "/CN=*.workshop.local" \ + -config <(printf "[req]\ndistinguished_name=req\n[v3_req]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\n") \ + -extensions v3_req; then + + echo "โœ… Certificate generation completed successfully" + else + echo "โŒ Certificate generation failed" + echo " OpenSSL exit code: $?" + return 1 + fi + } + + setup_certificates_verify() { + echo "๐Ÿ” Verifying certificate files..." + + CERT_FILE="$CERT_DIR/workshop.crt" + KEY_FILE="$CERT_DIR/workshop.key" + + # Check if files exist + if [[ ! -f "$CERT_FILE" ]]; then + echo "โŒ Certificate file not found: $CERT_FILE" + ls -la "$CERT_DIR" || echo " Directory listing failed" + return 1 + fi + + if [[ ! -f "$KEY_FILE" ]]; then + echo "โŒ Key file not found: $KEY_FILE" + ls -la "$CERT_DIR" || echo " Directory listing failed" + return 1 + fi + + echo "โœ… Certificate files created successfully" + + # Show file details + echo " Certificate file details:" + ls -la "$CERT_FILE" + echo " Key file details:" + ls -la "$KEY_FILE" + + # Verify certificate content + echo " Verifying certificate content..." + if openssl x509 -in "$CERT_FILE" -text -noout >/dev/null 2>&1; then + echo "โœ… Certificate is valid X.509 format" + # Show certificate subject + openssl x509 -in "$CERT_FILE" -subject -noout 2>/dev/null || echo " Could not read certificate subject" + else + echo "โŒ Certificate file is not valid" + return 1 + fi + + # Verify key content + if openssl rsa -in "$KEY_FILE" -check -noout >/dev/null 2>&1; then + echo "โœ… Private key is valid" + else + echo "โŒ Private key is invalid" + return 1 + fi + + echo "๐ŸŽ‰ Certificate generation and verification complete!" + } + + setup_traefik() { + echo "๐Ÿš€ Step 5: Setting up Traefik..." + + setup_traefik_app || return 1 + setup_traefik_config || return 1 + setup_traefik_secrets || return 1 + setup_traefik_deploy || return 1 + setup_traefik_wait || return 1 + } + + setup_traefik_app() { + echo "๐Ÿ“ฆ Checking Traefik app..." + + if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then + echo "๐Ÿš€ Creating Traefik app for OFFLINE use..." + echo " Command: sudo abra app new traefik --domain=traefik.workshop.local --server=default" + + if sudo abra app new traefik --domain=traefik.workshop.local --server=default; then + echo "โœ… Traefik app created successfully" + else + echo "โŒ Failed to create Traefik app" + echo " abra exit code: $?" + sudo abra app ls 2>&1 || echo " Could not list apps" + return 1 + fi + else + echo "โœ… Traefik app already exists" + fi + } + + setup_traefik_config() { + echo "โš™๏ธ Configuring Traefik for offline mode..." + + TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env" + echo " Config file: $TRAEFIK_ENV" + + # Check if config file exists + if [[ -f "$TRAEFIK_ENV" ]]; then + echo " โš ๏ธ Config file already exists, backing up..." + cp "$TRAEFIK_ENV" "$TRAEFIK_ENV.backup" || echo " Backup failed, continuing..." + fi + + # Create offline-friendly traefik configuration + echo " Writing offline configuration..." + if sudo tee -a "$TRAEFIK_ENV" >/dev/null </dev/null 2>&1 || \ - curl -s http://traefik.workshop.local/ping >/dev/null 2>&1; then - echo "โœ… Traefik ready! Dashboard: https://traefik.workshop.local (accept self-signed cert)" - echo "๐Ÿ’ก For HTTP: http://traefik.workshop.local" - return 0 - fi - sleep 2 - done - - echo "โš ๏ธ Traefik may still be starting. Check: sudo abra app logs traefik.workshop.local" - else - echo "โœ… Traefik already exists" - fi - - # Cleanup temporary certs - rm -rf "$CERT_DIR" 2>/dev/null || true - } + then + echo "โœ… Traefik configuration written successfully" + echo " Config file contents:" + sudo cat "$TRAEFIK_ENV" | head -20 + else + echo "โŒ Failed to write Traefik configuration" + echo " Target file: $TRAEFIK_ENV" + ls -la "$(dirname "$TRAEFIK_ENV")" 2>/dev/null || echo " Parent directory not accessible" + return 1 + fi + } + + setup_traefik_secrets() { + echo "๐Ÿ“‹ Installing self-signed certificates as Docker secrets..." + + # Verify certificate files exist + if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then + echo "โŒ Certificate file not found: $CERT_DIR/workshop.crt" + ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" + return 1 + fi + + if [[ ! -f "$CERT_DIR/workshop.key" ]]; then + echo "โŒ Key file not found: $CERT_DIR/workshop.key" + ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible" + return 1 + fi + + echo " Certificate files verified:" + ls -la "$CERT_DIR/workshop.crt" "$CERT_DIR/workshop.key" + + # Insert SSL certificate secret + echo " ๐Ÿ” Inserting SSL certificate secret..." + echo " Command: sudo abra app secret insert traefik.workshop.local ssl_cert v1" + + if sudo abra app secret insert traefik.workshop.local ssl_cert v1 -f < "$CERT_DIR/workshop.crt"; then + echo "โœ… SSL certificate secret inserted successfully" + else + echo "โŒ Failed to insert SSL certificate secret" + echo " abra exit code: $?" + echo " Checking abra app status..." + sudo abra app ls 2>&1 || echo " Could not list apps" + echo " Checking certificate file..." + file "$CERT_DIR/workshop.crt" 2>/dev/null || echo " Could not check certificate file type" + return 1 + fi + + # Insert SSL key secret + echo " ๐Ÿ”‘ Inserting SSL key secret..." + echo " Command: sudo abra app secret insert traefik.workshop.local ssl_key v1" + + if sudo abra app secret insert traefik.workshop.local ssl_key v1 -f < "$CERT_DIR/workshop.key"; then + echo "โœ… SSL key secret inserted successfully" + else + echo "โŒ Failed to insert SSL key secret" + echo " abra exit code: $?" + echo " Checking abra app status..." + sudo abra app ls 2>&1 || echo " Could not list apps" + echo " Checking key file..." + file "$CERT_DIR/workshop.key" 2>/dev/null || echo " Could not check key file type" + return 1 + fi + + echo "๐ŸŽ‰ All secrets inserted successfully!" + } + + setup_traefik_deploy() { + echo "๐Ÿš€ Deploying Traefik..." + + echo " Command: sudo abra app deploy traefik.workshop.local" + + if sudo abra app deploy traefik.workshop.local; then + echo "โœ… Traefik deployment initiated successfully" + else + echo "โŒ Traefik deployment failed" + echo " abra exit code: $?" + echo " Checking deployment status..." + sudo abra app ps traefik.workshop.local 2>&1 || echo " Could not check app status" + return 1 + fi + } + + setup_traefik_wait() { + echo "โณ Waiting for Traefik to be ready..." + + for i in {1..30}; do + echo " Checking Traefik status (attempt $i/30)..." + + # Try HTTPS first + if curl -s -k --max-time 5 https://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โœ… Traefik ready via HTTPS!" + echo " Dashboard: https://traefik.workshop.local (accept self-signed cert)" + echo " ๐Ÿ’ก For HTTP: http://traefik.workshop.local" + break + fi + + # Try HTTP as fallback + if curl -s --max-time 5 http://traefik.workshop.local/ping >/dev/null 2>&1; then + echo "โœ… Traefik ready via HTTP!" + echo " Dashboard: http://traefik.workshop.local" + echo " ๐Ÿ’ก For HTTPS: https://traefik.workshop.local (may require accepting cert)" + break + fi + + if [[ $i -eq 30 ]]; then + echo "โŒ Traefik failed to respond after 30 attempts" + echo " ๐Ÿ” Debug commands:" + echo " sudo abra app logs traefik.workshop.local" + echo " sudo abra app ps traefik.workshop.local" + echo " docker service ls | grep traefik" + return 1 + fi + + sleep 2 + done + + # Cleanup temporary certs + echo "๐Ÿงน Cleaning up temporary certificate files..." + if rm -rf "$CERT_DIR" 2>/dev/null; then + echo "โœ… Certificate cleanup completed" + else + echo "โš ๏ธ Certificate cleanup failed (non-critical)" + fi + + echo "๐ŸŽ‰ Traefik setup complete!" + } deploy() { if [[ -z "$1" ]]; then