|
1 | 1 | #!/bin/bash |
2 | 2 | # Generate Self-Signed SSL Certificates for Torrust Tracker Demo |
| 3 | +# |
| 4 | +# This script generates self-signed SSL certificates on the host filesystem |
| 5 | +# for local development and testing. These certificates provide HTTPS security |
| 6 | +# but will show browser warnings as they are not issued by a trusted CA. |
| 7 | +# |
| 8 | +# Usage: ./ssl-generate-test-certs.sh DOMAIN |
| 9 | +# |
| 10 | +# Arguments: |
| 11 | +# DOMAIN The domain name for which to generate certificates |
| 12 | +# |
| 13 | +# Examples: |
| 14 | +# ./ssl-generate-test-certs.sh test.local |
| 15 | +# ./ssl-generate-test-certs.sh example.com |
| 16 | + |
3 | 17 | set -euo pipefail |
4 | 18 |
|
5 | | -# Import common functions |
6 | | -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
7 | | -source "${SCRIPT_DIR}/../dev/shell-utils.sh" |
| 19 | +# Source application-specific shell utilities |
| 20 | +source "$(dirname "${BASH_SOURCE[0]}")/shell-utils.sh" |
8 | 21 |
|
9 | 22 | # Configuration |
10 | | -DOMAIN="${1:-}" |
| 23 | +DOMAIN="" |
| 24 | +CERT_VALIDITY_DAYS=365 |
| 25 | +KEY_SIZE=2048 |
| 26 | + |
| 27 | +# Parse command line arguments |
| 28 | +parse_arguments() { |
| 29 | + if [[ $# -ne 1 ]]; then |
| 30 | + log_error "Invalid number of arguments" |
| 31 | + show_usage |
| 32 | + exit 1 |
| 33 | + fi |
| 34 | + |
| 35 | + DOMAIN="$1" |
| 36 | +} |
| 37 | + |
| 38 | +# Show usage information |
| 39 | +show_usage() { |
| 40 | + cat << 'EOF' |
| 41 | +Generate Self-Signed SSL Certificates for Torrust Tracker Demo |
| 42 | +
|
| 43 | +This script generates self-signed SSL certificates on the host filesystem |
| 44 | +for local development and testing. The certificates are valid for HTTPS but |
| 45 | +will show security warnings in browsers as they are not issued by a trusted |
| 46 | +Certificate Authority. |
| 47 | +
|
| 48 | +USAGE: |
| 49 | + $0 DOMAIN |
| 50 | +
|
| 51 | +ARGUMENTS: |
| 52 | + DOMAIN Domain name for SSL certificates (e.g., test.local) |
| 53 | +
|
| 54 | +EXAMPLES: |
| 55 | + # Generate certificates for local testing |
| 56 | + $0 test.local |
| 57 | +
|
| 58 | + # Generate certificates for a custom domain |
| 59 | + $0 example.com |
| 60 | +
|
| 61 | +GENERATED CERTIFICATES: |
| 62 | + The script generates certificates for: |
| 63 | + - tracker.DOMAIN (Torrust Tracker API and web interface) |
| 64 | + - grafana.DOMAIN (Grafana monitoring dashboard) |
| 65 | +
|
| 66 | +CERTIFICATE LOCATIONS: |
| 67 | + Certificates are generated on the host filesystem at: |
| 68 | + - /var/lib/torrust/proxy/certs/tracker.DOMAIN.crt |
| 69 | + - /var/lib/torrust/proxy/private/tracker.DOMAIN.key |
| 70 | + - /var/lib/torrust/proxy/certs/grafana.DOMAIN.crt |
| 71 | + - /var/lib/torrust/proxy/private/grafana.DOMAIN.key |
| 72 | +
|
| 73 | +PREREQUISITES: |
| 74 | + 1. OpenSSL must be available on the host system |
| 75 | + 2. Write access to /var/lib/torrust/proxy/ directory |
| 76 | + 3. Running from the application directory (where compose.yaml is located) |
| 77 | +
|
| 78 | +SECURITY NOTE: |
| 79 | + Self-signed certificates provide encryption but not identity verification. |
| 80 | + They are suitable for development and testing but should be replaced with |
| 81 | + trusted certificates (Let's Encrypt) for production use. |
| 82 | +EOF |
| 83 | +} |
| 84 | + |
| 85 | +# Validate arguments |
| 86 | +validate_arguments() { |
| 87 | + if [[ -z "${DOMAIN}" ]]; then |
| 88 | + log_error "Domain name is required" |
| 89 | + show_usage |
| 90 | + exit 1 |
| 91 | + fi |
| 92 | + |
| 93 | + # Validate domain format (basic check) |
| 94 | + if [[ ! "${DOMAIN}" =~ ^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$ ]]; then |
| 95 | + log_error "Invalid domain format: ${DOMAIN}" |
| 96 | + exit 1 |
| 97 | + fi |
| 98 | +} |
| 99 | + |
| 100 | +# Check prerequisites |
| 101 | +check_prerequisites() { |
| 102 | + log_info "Checking prerequisites..." |
| 103 | + |
| 104 | + # Check if we're in the application directory |
| 105 | + if [[ ! -f "compose.yaml" ]]; then |
| 106 | + log_error "This script must be run from the application directory" |
| 107 | + log_error "Expected to find compose.yaml in current directory" |
| 108 | + exit 1 |
| 109 | + fi |
| 110 | + |
| 111 | + # Check if OpenSSL is available on the host |
| 112 | + if ! command -v openssl >/dev/null 2>&1; then |
| 113 | + log_error "OpenSSL is not available on the system" |
| 114 | + log_error "Please install OpenSSL: sudo apt update && sudo apt install openssl" |
| 115 | + exit 1 |
| 116 | + fi |
| 117 | + |
| 118 | + log_success "Prerequisites check passed" |
| 119 | +} |
| 120 | + |
| 121 | +# Generate self-signed certificate for a subdomain |
| 122 | +generate_certificate() { |
| 123 | + local subdomain="$1" |
| 124 | + local cert_path="/var/lib/torrust/proxy/certs/${subdomain}.crt" |
| 125 | + local key_path="/var/lib/torrust/proxy/private/${subdomain}.key" |
| 126 | + local config_path="/tmp/cert_config_${subdomain}.conf" |
| 127 | + |
| 128 | + log_info "Generating self-signed certificate for ${subdomain}..." |
| 129 | + |
| 130 | + # Generate private key |
| 131 | + log_info " - Generating private key..." |
| 132 | + if ! openssl genrsa -out "${key_path}" "${KEY_SIZE}"; then |
| 133 | + log_error "Failed to generate private key for ${subdomain}" |
| 134 | + return 1 |
| 135 | + fi |
| 136 | + |
| 137 | + # Set secure permissions on private key |
| 138 | + chmod 600 "${key_path}" |
| 139 | + |
| 140 | + # Generate certificate configuration |
| 141 | + log_info " - Creating certificate configuration..." |
| 142 | + cat > "${config_path}" << EOF |
| 143 | +[req] |
| 144 | +distinguished_name = req_distinguished_name |
| 145 | +req_extensions = v3_req |
| 146 | +prompt = no |
| 147 | +
|
| 148 | +[req_distinguished_name] |
| 149 | +C=US |
| 150 | +ST=Test |
| 151 | +L=Test |
| 152 | +O=Torrust Tracker Demo |
| 153 | +OU=Testing |
| 154 | +CN=${subdomain} |
| 155 | +
|
| 156 | +[v3_req] |
| 157 | +keyUsage = keyEncipherment, dataEncipherment |
| 158 | +extendedKeyUsage = serverAuth |
| 159 | +subjectAltName = @alt_names |
| 160 | +
|
| 161 | +[alt_names] |
| 162 | +DNS.1 = ${subdomain} |
| 163 | +DNS.2 = localhost |
| 164 | +IP.1 = 127.0.0.1 |
| 165 | +EOF |
| 166 | + |
| 167 | + # Generate self-signed certificate |
| 168 | + log_info " - Generating self-signed certificate..." |
| 169 | + if ! openssl req -new -x509 \ |
| 170 | + -key "${key_path}" \ |
| 171 | + -out "${cert_path}" \ |
| 172 | + -days "${CERT_VALIDITY_DAYS}" \ |
| 173 | + -config "${config_path}" \ |
| 174 | + -extensions v3_req; then |
| 175 | + log_error "Failed to generate certificate for ${subdomain}" |
| 176 | + rm -f "${config_path}" |
| 177 | + return 1 |
| 178 | + fi |
| 179 | + |
| 180 | + # Clean up temporary config file |
| 181 | + rm -f "${config_path}" |
| 182 | + |
| 183 | + # Set appropriate permissions on certificate |
| 184 | + chmod 644 "${cert_path}" |
| 185 | + |
| 186 | + log_success " ✅ Certificate generated for ${subdomain}" |
| 187 | + log_info " Private key: ${key_path}" |
| 188 | + log_info " Certificate: ${cert_path}" |
| 189 | + return 0 |
| 190 | +} |
| 191 | + |
| 192 | +# Show certificate information |
| 193 | +show_certificate_info() { |
| 194 | + local subdomain="$1" |
| 195 | + local cert_path="/var/lib/torrust/proxy/certs/${subdomain}.crt" |
| 196 | + |
| 197 | + log_info "Certificate information for ${subdomain}:" |
| 198 | + log_info " Location: ${cert_path}" |
| 199 | + log_info " Type: Self-signed certificate" |
| 200 | + log_info " Validity: ${CERT_VALIDITY_DAYS} days" |
| 201 | + |
| 202 | + # Try to show certificate details |
| 203 | + if [[ -f "${cert_path}" ]]; then |
| 204 | + local subject |
| 205 | + local expiry |
| 206 | + subject=$(openssl x509 -in "${cert_path}" -noout -subject 2>/dev/null | cut -d= -f2- || echo "Unable to determine") |
| 207 | + expiry=$(openssl x509 -in "${cert_path}" -noout -enddate 2>/dev/null | cut -d= -f2 || echo "Unable to determine") |
| 208 | + log_info " Subject: ${subject}" |
| 209 | + log_info " Expires: ${expiry}" |
| 210 | + fi |
| 211 | +} |
| 212 | + |
| 213 | +# Main certificate generation function |
| 214 | +main() { |
| 215 | + log_info "Starting self-signed SSL certificate generation" |
| 216 | + log_info "Domain: ${DOMAIN}" |
| 217 | + log_info "Validity: ${CERT_VALIDITY_DAYS} days" |
| 218 | + |
| 219 | + check_prerequisites |
| 220 | + |
| 221 | + # Create SSL certificate directories if they don't exist |
| 222 | + local cert_dir="/var/lib/torrust/proxy/certs" |
| 223 | + local private_dir="/var/lib/torrust/proxy/private" |
| 224 | + if [[ ! -d "${cert_dir}" ]] || [[ ! -d "${private_dir}" ]]; then |
| 225 | + log_info "Creating SSL certificate directories..." |
| 226 | + sudo mkdir -p "${cert_dir}" "${private_dir}" |
| 227 | + sudo chown -R torrust:torrust /var/lib/torrust/proxy/ |
| 228 | + sudo chmod 755 "${cert_dir}" |
| 229 | + sudo chmod 700 "${private_dir}" |
| 230 | + fi |
| 231 | + |
| 232 | + # Generate certificates for required subdomains |
| 233 | + local subdomains=("tracker.${DOMAIN}" "grafana.${DOMAIN}") |
| 234 | + local generation_failed=false |
| 235 | + |
| 236 | + for subdomain in "${subdomains[@]}"; do |
| 237 | + if ! generate_certificate "${subdomain}"; then |
| 238 | + generation_failed=true |
| 239 | + fi |
| 240 | + done |
| 241 | + |
| 242 | + # Check if any certificate generation failed |
| 243 | + if [[ "${generation_failed}" == "true" ]]; then |
| 244 | + log_error "Certificate generation failed for one or more subdomains" |
| 245 | + log_error "Please check the error messages above and resolve any issues" |
| 246 | + exit 1 |
| 247 | + fi |
| 248 | + |
| 249 | + # Show certificate information |
| 250 | + log_info "" |
| 251 | + log_info "Certificate generation completed successfully!" |
| 252 | + for subdomain in "${subdomains[@]}"; do |
| 253 | + show_certificate_info "${subdomain}" |
| 254 | + done |
| 255 | + |
| 256 | + # Show next steps |
| 257 | + log_info "" |
| 258 | + log_info "Next steps:" |
| 259 | + log_info "1. Start Docker services that will use these certificates" |
| 260 | + log_info "2. Test HTTPS endpoints (expect certificate warnings in browsers)" |
| 261 | + log_info "3. To upgrade to trusted certificates later, use Let's Encrypt SSL setup" |
| 262 | + log_info "" |
| 263 | + log_warning "⚠️ Self-signed certificate security notes:" |
| 264 | + log_warning " - Browsers will show security warnings" |
| 265 | + log_warning " - These certificates provide encryption but not identity verification" |
| 266 | + log_warning " - Suitable for development/testing, not production use" |
| 267 | + log_warning " - For production, use Let's Encrypt certificates instead" |
11 | 268 |
|
12 | | -if [[ -z "${DOMAIN}" ]]; then |
13 | | - echo "Usage: $0 DOMAIN" |
14 | | - exit 1 |
15 | | -fi |
| 269 | + log_success "✅ Self-signed SSL certificate generation completed successfully!" |
| 270 | +} |
16 | 271 |
|
17 | | -log_info "Generating self-signed certificates for ${DOMAIN}" |
18 | | -log_success "✅ Certificate generation completed" |
| 272 | +# Parse arguments and run main function |
| 273 | +parse_arguments "$@" |
| 274 | +validate_arguments |
| 275 | +main |
0 commit comments