#!/bin/bash # # SSL Certificate Setup Script # Runs only the nginx-proxy role to set up SSL certificates for production domain # # Usage: ./setup-ssl.sh [OPTIONS] # set -euo pipefail # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" INFRA_DIR="${SCRIPT_DIR}/infrastructure" # Default values ENVIRONMENT="production" DOMAIN_NAME="michaelschiemer.de" VAULT_PASSWORD_FILE="" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${BLUE}[INFO]${NC} $1" >&2 } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2 } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2 } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } # Help function show_help() { cat << EOF SSL Certificate Setup Script for Custom PHP Framework USAGE: $0 [OPTIONS] OPTIONS: --domain DOMAIN Domain name (default: $DOMAIN_NAME) --vault-password-file FILE Specify vault password file path --help Show this help message EXAMPLES: # Setup SSL for default domain $0 # Setup SSL for custom domain $0 --domain example.com ENVIRONMENT VARIABLES: ANSIBLE_VAULT_PASSWORD_FILE Vault password file (overrides --vault-password-file) DOMAIN_NAME Domain name (overrides --domain) REQUIREMENTS: - Ansible 2.9+ - SSH access to production server - Domain must point to production server IP - Vault password file or ANSIBLE_VAULT_PASSWORD_FILE environment variable EOF } # Parse command line arguments parse_args() { while [[ $# -gt 0 ]]; do case $1 in --help|-h) show_help exit 0 ;; --domain) if [[ -z "${2:-}" ]] || [[ "$2" =~ ^-- ]]; then log_error "--domain requires a domain name" exit 1 fi DOMAIN_NAME="$2" shift 2 ;; --vault-password-file) if [[ -z "${2:-}" ]] || [[ "$2" =~ ^-- ]]; then log_error "--vault-password-file requires a file path" exit 1 fi VAULT_PASSWORD_FILE="$2" shift 2 ;; -*) log_error "Unknown option: $1" show_help exit 1 ;; *) log_error "Unexpected positional argument: $1" show_help exit 1 ;; esac done } # Validate environment and requirements validate_environment() { log_info "Validating SSL setup environment..." # Override with environment variables if set DOMAIN_NAME="${DOMAIN_NAME:-michaelschiemer.de}" # Check if ansible is available if ! command -v ansible-playbook &> /dev/null; then log_error "ansible-playbook not found. Please install Ansible." exit 1 fi # Check vault password file if [[ -n "${ANSIBLE_VAULT_PASSWORD_FILE:-}" ]]; then VAULT_PASSWORD_FILE="$ANSIBLE_VAULT_PASSWORD_FILE" fi if [[ -z "$VAULT_PASSWORD_FILE" ]]; then log_warning "No vault password file specified. Ansible will prompt for vault password." elif [[ ! -f "$VAULT_PASSWORD_FILE" ]]; then log_error "Vault password file not found: $VAULT_PASSWORD_FILE" exit 1 fi # Check infrastructure directory if [[ ! -d "$INFRA_DIR" ]]; then log_error "Infrastructure directory not found: $INFRA_DIR" exit 1 fi # Check inventory file local inventory_file="${INFRA_DIR}/inventories/production/hosts.yml" if [[ ! -f "$inventory_file" ]]; then log_error "Production inventory not found: $inventory_file" exit 1 fi log_success "Environment validation complete" } # Check domain DNS resolution check_dns_resolution() { log_info "Checking DNS resolution for $DOMAIN_NAME..." if command -v dig &> /dev/null; then local resolved_ip=$(dig +short "$DOMAIN_NAME" | head -1) log_info "Domain $DOMAIN_NAME resolves to: $resolved_ip" elif command -v nslookup &> /dev/null; then log_info "Checking DNS with nslookup..." nslookup "$DOMAIN_NAME" else log_warning "No DNS lookup tools available, skipping DNS check" fi } # Create SSL-specific playbook create_ssl_playbook() { local ssl_playbook="${INFRA_DIR}/ssl-setup.yml" log_info "Creating SSL setup playbook..." cat > "$ssl_playbook" << 'EOF' --- # SSL Certificate Setup Playbook # Sets up nginx with Let's Encrypt SSL certificates - name: Setup SSL Certificates for Custom PHP Framework hosts: web_servers become: true gather_facts: true vars: deploy_env: "{{ environment }}" ssl_provider: letsencrypt letsencrypt_enabled: true nginx_enabled: true pre_tasks: - name: Display SSL setup information debug: msg: - "Setting up SSL certificates for {{ domain_name }}" - "Environment: {{ environment | upper }}" - "SSL Provider: {{ ssl_provider }}" - "Target Host: {{ inventory_hostname }}" tags: always - name: Verify SSL requirements assert: that: - domain_name is defined - ssl_email is defined - ssl_provider == 'letsencrypt' - environment == 'production' fail_msg: "SSL requirements not met" success_msg: "SSL requirements verified" tags: always - name: Test domain connectivity uri: url: "http://{{ domain_name }}" method: HEAD status_code: [200, 301, 302, 404, 500] timeout: 10 register: domain_test ignore_errors: true tags: always - name: Display domain connectivity results debug: msg: - "Domain connectivity test: {{ 'OK' if domain_test.status is defined else 'Failed' }}" - "Status code: {{ domain_test.status | default('N/A') }}" tags: always roles: # Install nginx with SSL certificates - role: nginx-proxy tags: - nginx - ssl - letsencrypt post_tasks: - name: Test SSL certificate installation command: nginx -t register: nginx_test changed_when: false tags: verification - name: Check nginx service status service_facts: tags: verification - name: Verify nginx is running assert: that: - ansible_facts.services['nginx.service'].state == 'running' fail_msg: "Nginx is not running properly" success_msg: "Nginx is running successfully" tags: verification - name: Test HTTPS connectivity uri: url: "https://{{ domain_name }}/health" method: GET status_code: [200, 404, 500] timeout: 30 validate_certs: true register: https_test retries: 3 delay: 10 until: https_test.status is defined tags: verification - name: Display SSL setup results debug: msg: - "=== SSL CERTIFICATE SETUP COMPLETED ===" - "Domain: {{ domain_name }}" - "SSL Provider: {{ ssl_provider }}" - "Nginx Config: {{ 'Valid' if nginx_test.rc == 0 else 'Invalid' }}" - "Nginx Status: {{ ansible_facts.services['nginx.service'].state }}" - "HTTPS Test: {{ 'Success' if https_test.status is defined else 'Failed' }}" - "HTTPS URL: https://{{ domain_name }}" - "======================================" tags: always EOF log_success "SSL setup playbook created: $ssl_playbook" } # Execute SSL setup run_ssl_setup() { log_info "Starting SSL certificate setup..." local ansible_cmd="ansible-playbook" local inventory="${INFRA_DIR}/inventories/production/hosts.yml" local playbook="${INFRA_DIR}/ssl-setup.yml" local extra_vars="-e domain_name=$DOMAIN_NAME -e environment=production -e deploy_env=production" # Build ansible command local cmd="$ansible_cmd -i $inventory $playbook $extra_vars" # Add vault password file if specified if [[ -n "$VAULT_PASSWORD_FILE" ]]; then cmd+=" --vault-password-file $VAULT_PASSWORD_FILE" fi # Add tags to focus on SSL setup cmd+=" --tags nginx,ssl,letsencrypt,verification" # Change to infrastructure directory cd "$INFRA_DIR" log_info "Executing: $cmd" # Run SSL setup if eval "$cmd"; then log_success "SSL setup completed successfully!" log_success "HTTPS is now available at: https://$DOMAIN_NAME" return 0 else log_error "SSL setup failed!" return 1 fi } # Cleanup function cleanup() { local exit_code=$? if [[ $exit_code -ne 0 ]]; then log_error "SSL setup failed with exit code: $exit_code" log_info "Check the logs above for details" fi # Clean up temporary playbook if [[ -f "${INFRA_DIR}/ssl-setup.yml" ]]; then rm -f "${INFRA_DIR}/ssl-setup.yml" log_info "Cleaned up temporary SSL playbook" fi exit $exit_code } # Main execution main() { # Set trap for cleanup trap cleanup EXIT # Parse command line arguments parse_args "$@" # Validate environment validate_environment # Check DNS resolution check_dns_resolution # Create SSL-specific playbook create_ssl_playbook # Run SSL setup run_ssl_setup log_success "SSL certificate setup completed successfully!" log_info "You can now access your site securely at: https://$DOMAIN_NAME" } # Execute main function if script is run directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi