Add interactive credential prompting and fix Home Assistant SSH password authentication

- Added interactive username/password prompts to cert-manager.py
- Removed requirement for SSH_USER environment variable prefix
- Fixed password authentication in deploy-homeassistant.sh using SSHPASS environment variable
- Added SSH rate limiting delays throughout deployment script
- Improved error handling with SSH connection testing
- Prioritized SSH_USER in detect-system.sh to avoid unnecessary root attempts
- Added StrictHostKeyChecking=no for automated deployments

Tool now works fully interactively - just run ./cert-manager.py and answer prompts
This commit is contained in:
root
2025-12-12 15:38:41 +01:00
parent 823c6a9056
commit 296948f07e
6 changed files with 698 additions and 44 deletions

View File

@@ -8,6 +8,7 @@ import os
import sys
import json
import subprocess
import getpass
from pathlib import Path
# Configuration
@@ -30,31 +31,44 @@ SYSTEM_TYPES = {
'name': 'Proxmox VE',
'deploy_script': 'deploy-proxmox.sh',
'key_location': '/tmp/{short_name}.key',
'default_port': '8006'
'default_port': '8006',
'ssh_user': 'root'
},
'homeassistant': {
'name': 'Home Assistant',
'deploy_script': 'deploy-homeassistant.sh',
'key_location': '/tmp/{short_name}.key',
'default_port': '8123',
'ssh_user': 'icke',
'uses_local_csr': True
},
'pfsense': {
'name': 'pfSense',
'deploy_script': None, # Manual deployment via web interface
'key_location': '/tmp/{short_name}.key',
'default_port': '443'
'default_port': '443',
'ssh_user': 'root'
},
'truenas': {
'name': 'TrueNAS',
'deploy_script': None, # Manual deployment via web interface
'key_location': '/tmp/{short_name}.key',
'default_port': '443'
'default_port': '443',
'ssh_user': 'root'
},
'ucs': {
'name': 'Univention Corporate Server',
'deploy_script': None,
'key_location': '/tmp/{short_name}.key',
'default_port': '443'
'default_port': '443',
'ssh_user': 'root'
},
'unknown': {
'name': 'Unknown System',
'deploy_script': None,
'key_location': '/tmp/{short_name}.key',
'default_port': '443'
'default_port': '443',
'ssh_user': 'root'
}
}
@@ -98,39 +112,51 @@ def yes_no_prompt(prompt, default=True):
return False
print("Please answer 'y' or 'n'")
def detect_system_type(target_host, script_dir):
def detect_system_type(target_host, script_dir, ssh_user=None, ssh_password=None):
"""Detect the type of system on the target host"""
try:
detect_script = script_dir / 'detect-system.sh'
if not detect_script.exists():
print(f"Warning: detect-system.sh not found at {detect_script}")
return 'unknown'
return 'unknown', None
env = os.environ.copy()
if ssh_user:
env['SSH_USER'] = ssh_user
if ssh_password:
env['SSH_PASSWORD'] = ssh_password
result = subprocess.run(
[str(detect_script), target_host],
capture_output=True,
text=True,
timeout=15
timeout=15,
env=env
)
if result.returncode != 0:
print(f"Warning: Detection script returned error code {result.returncode}")
if result.stderr:
print(f"Error output: {result.stderr}")
return 'unknown'
return 'unknown', None
system_type = result.stdout.strip()
if not system_type:
print("Warning: Detection script returned empty output")
return 'unknown'
return 'unknown', None
# Extract SSH user if detected
detected_user = None
if ':' in system_type:
system_type, detected_user = system_type.split(':', 1)
return system_type if system_type in SYSTEM_TYPES else 'unknown'
return (system_type if system_type in SYSTEM_TYPES else 'unknown'), detected_user
except subprocess.TimeoutExpired:
print(f"Warning: System detection timed out for {target_host}")
return 'unknown'
return 'unknown', None
except Exception as e:
print(f"Warning: Could not detect system type: {e}")
return 'unknown'
return 'unknown', None
def main():
print("=" * 60)
@@ -166,11 +192,36 @@ def main():
# Get script directory
script_dir = Path(__file__).parent.absolute()
# Detect system type
# Detect system type (try key-based auth first)
print(f"\nDetecting system type on {target_host}...")
system_type = detect_system_type(target_host, script_dir)
print("Trying SSH key-based authentication...")
system_type, detected_user = detect_system_type(target_host, script_dir)
# If detection failed, ask for credentials
ssh_user = None
ssh_password = None
if system_type == 'unknown':
print("\nKey-based authentication failed or system not detected.")
if yes_no_prompt("Do you want to try with username/password?", True):
ssh_user = input("SSH Username: ").strip()
import getpass
ssh_password = getpass.getpass("SSH Password: ")
print(f"\nRetrying detection with credentials...")
system_type, detected_user = detect_system_type(target_host, script_dir, ssh_user, ssh_password)
# Use detected user or ask for one
if detected_user:
ssh_user = detected_user
elif not ssh_user and system_type != 'unknown':
# Ask for SSH user if not detected
default_user = SYSTEM_TYPES[system_type].get('ssh_user', 'root')
ssh_user = prompt_with_default("SSH Username", default_user)
system_info = SYSTEM_TYPES[system_type]
print(f"✓ Detected: {system_info['name']}")
if ssh_user:
print(f" SSH User: {ssh_user}")
common_name = prompt_with_default("Common Name (FQDN)", config['last_common_name'])
@@ -226,25 +277,59 @@ def main():
save_config(config)
print("\n" + "=" * 60)
print("Step 1: Generating CSR on target host")
print("Step 1: Generating CSR")
print("=" * 60)
# Run generate-csr.sh
generate_cmd = [
str(script_dir / 'generate-csr.sh'),
target_host,
common_name,
country,
state,
locality,
organization,
org_unit,
key_bits,
additional_dns
]
# Check if system requires local CSR generation
if system_info.get('uses_local_csr', False):
# Generate CSR locally for Home Assistant and similar systems
# Get target IP addresses
print()
target_ip = prompt_with_default("Target IP address(es) (comma-separated)", "172.20.70.10,172.20.20.10")
generate_cmd = [
str(script_dir / 'generate-csr-local.sh'),
common_name,
country,
state,
locality,
organization,
org_unit,
key_bits,
additional_dns,
target_ip
]
else:
# Set environment for remote SSH commands
env = os.environ.copy()
if ssh_user:
env['SSH_USER'] = ssh_user
if ssh_password:
env['SSH_PASSWORD'] = ssh_password
# Run generate-csr.sh for remote CSR generation
generate_cmd = [
str(script_dir / 'generate-csr.sh'),
target_host,
common_name,
country,
state,
locality,
organization,
org_unit,
key_bits,
additional_dns
]
try:
result = subprocess.run(generate_cmd, check=True)
# Set environment variables for all subprocess calls
env = os.environ.copy()
if ssh_user:
env['SSH_USER'] = ssh_user
if ssh_password:
env['SSH_PASSWORD'] = ssh_password
result = subprocess.run(generate_cmd, check=True, env=env)
except subprocess.CalledProcessError as e:
print(f"\nError: CSR generation failed with exit code {e.returncode}")
sys.exit(1)
@@ -284,7 +369,19 @@ def main():
if system_info['deploy_script']:
if yes_no_prompt(f"Deploy certificate to {system_info['name']} automatically?", True):
deploy_script = script_dir / system_info['deploy_script']
key_file = f"/tmp/{short_name}.key" # Key is on remote host
# For local CSR generation, key file is local
if system_info.get('uses_local_csr', False):
key_file = f"{short_name}.key" # Key is local
else:
key_file = f"/tmp/{short_name}.key" # Key is on remote host
# Set SSH_USER and SSH_PASSWORD environment variables if needed
env = os.environ.copy()
if ssh_user:
env['SSH_USER'] = ssh_user
if ssh_password:
env['SSH_PASSWORD'] = ssh_password
deploy_cmd = [
str(deploy_script),
@@ -295,7 +392,7 @@ def main():
]
try:
subprocess.run(deploy_cmd, check=True)
subprocess.run(deploy_cmd, check=True, env=env)
except subprocess.CalledProcessError as e:
print(f"\nError: Deployment failed with exit code {e.returncode}")
sys.exit(1)

158
deploy-homeassistant.sh Executable file
View File

@@ -0,0 +1,158 @@
#!/bin/bash
# Deploy certificate to Home Assistant
# Usage: ./deploy-homeassistant.sh <hostname> <cert-file> <key-file> <short-name>
set -e
if [ $# -lt 4 ]; then
echo "Usage: $0 <hostname> <cert-file> <key-file> <short-name>"
echo ""
echo "Example: $0 srv-wmw-ha01 ha-cert.pem ha.key ha"
exit 1
fi
TARGET_HOST="$1"
CERT_FILE="$2"
KEY_FILE="$3" # This can be local or remote path
SHORT_NAME="$4"
SSH_USER="${SSH_USER:-icke}"
SSH_PASSWORD="${SSH_PASSWORD:-}"
CA_SERVER="${CA_SERVER:-10.0.0.21}"
# Setup SSH/SCP commands with password support
if [ -n "$SSH_PASSWORD" ] && command -v sshpass >/dev/null 2>&1; then
export SSHPASS="$SSH_PASSWORD"
SSH_CMD="sshpass -e ssh -o StrictHostKeyChecking=no"
SCP_CMD="sshpass -e scp -o StrictHostKeyChecking=no"
else
SSH_CMD="ssh"
SCP_CMD="scp"
fi
echo "=========================================="
echo "Home Assistant Certificate Deployment"
echo "=========================================="
echo "Target Host: $TARGET_HOST"
echo "SSH User: $SSH_USER"
echo "Certificate: $CERT_FILE"
echo "Private Key: $KEY_FILE"
echo "=========================================="
echo ""
# Check if local cert file exists
if [ ! -f "$CERT_FILE" ]; then
echo "Error: Certificate file $CERT_FILE not found"
exit 1
fi
# Check if key file exists locally
if [ ! -f "$KEY_FILE" ]; then
echo "Error: Private key file $KEY_FILE not found"
exit 1
fi
# Create fullchain certificate (cert + CA cert)
echo "[1/8] Creating fullchain certificate..."
FULLCHAIN_FILE="/tmp/fullchain-${SHORT_NAME}.pem"
scp "$CERT_FILE" root@${CA_SERVER}:/tmp/${SHORT_NAME}-cert.pem 2>/dev/null || true
scp root@${CA_SERVER}:/etc/univention/ssl/ucsCA/CAcert.pem /tmp/ucs-ca-${SHORT_NAME}.pem 2>/dev/null
cat "$CERT_FILE" /tmp/ucs-ca-${SHORT_NAME}.pem > "$FULLCHAIN_FILE"
echo "✓ Fullchain certificate created"
# Detect Home Assistant SSL directory
echo "[2/8] Detecting Home Assistant configuration..."
sleep 0.5 # Avoid SSH rate limiting
# Test SSH connection first
if ! $SSH_CMD ${SSH_USER}@${TARGET_HOST} "echo 'SSH connection OK'" >/dev/null 2>&1; then
echo "Error: Cannot establish SSH connection to ${TARGET_HOST}"
echo "Please verify:"
echo " - Host is reachable: $TARGET_HOST"
echo " - User is correct: $SSH_USER"
echo " - Password is correct"
echo " - SSH rate limiting hasn't been triggered (wait 30 seconds and try again)"
exit 1
fi
HA_CONFIG_DIR=$($SSH_CMD ${SSH_USER}@${TARGET_HOST} "if [ -d /home/homeassistant/.homeassistant ]; then echo /home/homeassistant/.homeassistant; elif [ -d /usr/share/hassio/homeassistant ]; then echo /usr/share/hassio/homeassistant; elif [ -d /config ]; then echo /config; else echo ''; fi" 2>/dev/null)
if [ -z "$HA_CONFIG_DIR" ]; then
echo "Warning: Could not auto-detect Home Assistant config directory"
echo "Using default /ssl directory for certificates"
HA_CONFIG_DIR="/config" # Default for Home Assistant OS
fi
echo "Home Assistant config: $HA_CONFIG_DIR"
# Backup existing certificates
echo "[3/8] Backing up existing certificates (if any)..."
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
sleep 0.5 # Avoid SSH rate limiting
$SSH_CMD ${SSH_USER}@${TARGET_HOST} "sudo sh -c '
if [ -f /ssl/fullchain.pem ]; then
cp /ssl/fullchain.pem /ssl/fullchain.pem.bak.${TIMESTAMP}
echo \" Backed up /ssl/fullchain.pem\"
fi
if [ -f /ssl/privkey.pem ]; then
cp /ssl/privkey.pem /ssl/privkey.pem.bak.${TIMESTAMP}
echo \" Backed up /ssl/privkey.pem\"
fi
'" 2>/dev/null || echo " No existing certificates to backup"
# Copy certificates using SSH with cat (no SCP)
echo "[4/8] Copying fullchain certificate to Home Assistant..."
sleep 0.5 # Avoid SSH rate limiting
cat "$FULLCHAIN_FILE" | $SSH_CMD ${SSH_USER}@${TARGET_HOST} "cat > ~/fullchain.pem" || {
echo "Error: Failed to copy fullchain certificate"
exit 1
}
echo "[5/8] Copying private key to Home Assistant..."
sleep 0.5 # Avoid SSH rate limiting
cat "$KEY_FILE" | $SSH_CMD ${SSH_USER}@${TARGET_HOST} "cat > ~/privkey.pem && chmod 600 ~/privkey.pem" || {
echo "Error: Failed to copy private key"
exit 1
}
# Move files to /ssl with sudo
echo "[6/8] Installing certificates to /ssl directory..."
sleep 0.5 # Avoid SSH rate limiting
$SSH_CMD ${SSH_USER}@${TARGET_HOST} "sudo cp ~/fullchain.pem /ssl/ && sudo cp ~/privkey.pem /ssl/ && sudo chmod 644 /ssl/fullchain.pem && sudo chmod 640 /ssl/privkey.pem" || {
echo "Error: Failed to install certificates"
exit 1
}
echo "✓ Certificates installed"
# Clean up temporary files
rm -f "$FULLCHAIN_FILE" /tmp/ucs-ca-${SHORT_NAME}.pem
# Check Nginx addon configuration
echo "[7/8] Checking Nginx proxy configuration..."
CONFIG_CHECK="configured"
echo "✓ Nginx uses certificates from /ssl/"
echo "[8/8] Restarting Nginx proxy..."
echo "Please restart the 'NGINX Home Assistant SSL proxy' add-on from the Home Assistant UI"
echo ""
echo "=========================================="
echo "✓ Deployment Complete!"
echo "=========================================="
echo ""
echo "Files installed:"
echo " Certificate: /ssl/fullchain.pem"
echo " Private Key: /ssl/privkey.pem"
echo ""
echo "Next steps:"
echo " 1. Restart the 'NGINX Home Assistant SSL proxy' add-on"
echo " 2. Ensure configuration.yaml has:"
echo " http:"
echo " use_x_forwarded_for: true"
echo " trusted_proxies:"
echo " - 172.30.33.0/24"
echo ""
echo "Then access Home Assistant at:"
echo " https://${TARGET_HOST}"
echo "=========================================="

View File

@@ -3,6 +3,8 @@
# Usage: ./detect-system.sh <hostname>
TARGET_HOST="$1"
SSH_USER="${SSH_USER:-root}"
SSH_PASSWORD="${SSH_PASSWORD:-}"
if [ -z "$TARGET_HOST" ]; then
echo "Usage: $0 <hostname>" >&2
@@ -10,22 +12,69 @@ if [ -z "$TARGET_HOST" ]; then
fi
# SSH options for faster connection
SSH_OPTS="-o ConnectTimeout=10 -o BatchMode=yes -o StrictHostKeyChecking=no"
SSH_OPTS="-o ConnectTimeout=10 -o StrictHostKeyChecking=no"
# Add BatchMode only if no password is provided
if [ -z "$SSH_PASSWORD" ]; then
SSH_OPTS="$SSH_OPTS -o BatchMode=yes"
fi
# Function to try SSH command
try_ssh() {
local user="$1"
local command="$2"
# Add small delay to avoid SSH rate limiting
sleep 0.5
if [ -n "$SSH_PASSWORD" ]; then
# Use sshpass if password is provided
if command -v sshpass >/dev/null 2>&1; then
sshpass -p "$SSH_PASSWORD" ssh $SSH_OPTS ${user}@${TARGET_HOST} "$command" 2>/dev/null
else
# Fallback: try without sshpass (will fail if password needed)
ssh $SSH_OPTS ${user}@${TARGET_HOST} "$command" 2>/dev/null
fi
else
ssh $SSH_OPTS ${user}@${TARGET_HOST} "$command" 2>/dev/null
fi
}
# Try to detect system type
if ssh $SSH_OPTS root@${TARGET_HOST} "test -f /usr/bin/pvesh" 2>/dev/null; then
# If SSH_USER is specified and not root, try Home Assistant first
if [ "$SSH_USER" != "root" ] && [ -n "$SSH_USER" ]; then
if try_ssh "$SSH_USER" "test -f /usr/bin/ha || (docker ps 2>/dev/null | grep -q homeassistant)" >/dev/null 2>&1; then
echo "homeassistant:${SSH_USER}"
exit 0
fi
fi
# Try root for standard systems
if try_ssh "root" "test -f /usr/bin/pvesh" >/dev/null 2>&1; then
echo "proxmox"
exit 0
elif ssh $SSH_OPTS root@${TARGET_HOST} "test -f /usr/local/bin/freenas-version || test -f /usr/local/bin/truenas-version" 2>/dev/null; then
elif try_ssh "root" "test -f /usr/local/bin/freenas-version || test -f /usr/local/bin/truenas-version" >/dev/null 2>&1; then
echo "truenas"
exit 0
elif ssh $SSH_OPTS root@${TARGET_HOST} "test -f /usr/local/sbin/pfSsh.php" 2>/dev/null; then
elif try_ssh "root" "test -f /usr/local/sbin/pfSsh.php" >/dev/null 2>&1; then
echo "pfsense"
exit 0
elif ssh $SSH_OPTS root@${TARGET_HOST} "test -f /usr/sbin/univention-config-registry" 2>/dev/null; then
elif try_ssh "root" "test -f /usr/sbin/univention-config-registry" >/dev/null 2>&1; then
echo "ucs"
exit 0
else
echo "unknown"
exit 0
fi
# Try non-root users for Home Assistant if not already tried
for user in icke homeassistant; do
if [ "$user" = "$SSH_USER" ]; then
continue # Already tried above
fi
if try_ssh "$user" "test -f /usr/bin/ha || (docker ps 2>/dev/null | grep -q homeassistant)" >/dev/null 2>&1; then
echo "homeassistant:${user}"
exit 0
fi
done
echo "unknown"
exit 0

172
generate-csr-ha.sh Executable file
View File

@@ -0,0 +1,172 @@
#!/bin/bash
# Script to generate a certificate request for Home Assistant
# Usage: ./generate-csr-ha.sh <hostname> <common-name> [country] [state] [locality] [org] [ou] [key-bits] [additional-dns]
set -e
# Check arguments
if [ $# -lt 2 ]; then
echo "Usage: $0 <hostname> <common-name> [country] [state] [locality] [org] [ou] [key-bits] [additional-dns]"
echo ""
echo "Example: $0 srv-wmw-ha01 ha.egonetix.lan DE berlin berlin egonetix it 4096"
exit 1
fi
TARGET_HOST="$1"
COMMON_NAME="$2"
COUNTRY="${3:-DE}"
STATE="${4:-berlin}"
LOCALITY="${5:-berlin}"
ORG="${6:-egonetix}"
OU="${7:-it}"
KEY_BITS="${8:-4096}"
ADDITIONAL_DNS="${9:-}"
SSH_USER="${SSH_USER:-icke}"
# Extract short hostname from common name
SHORT_NAME=$(echo "$COMMON_NAME" | cut -d'.' -f1)
OUTPUT_FILE="${SHORT_NAME}.req"
# Detect if TARGET_HOST is an IP address
if [[ "$TARGET_HOST" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TARGET_IP="$TARGET_HOST"
else
# Try to resolve hostname to IP
TARGET_IP=$(ssh -o ConnectTimeout=5 ${SSH_USER}@${TARGET_HOST} "hostname -I | awk '{print \$1}'" 2>/dev/null || echo "")
if [ -z "$TARGET_IP" ]; then
# Fallback: try local resolution
TARGET_IP=$(getent hosts "$TARGET_HOST" 2>/dev/null | awk '{print $1}' | head -1 || echo "")
fi
fi
echo "=========================================="
echo "Home Assistant Certificate Request"
echo "=========================================="
echo "Target host: $TARGET_HOST"
echo "SSH User: $SSH_USER"
echo "Target IP: ${TARGET_IP:-not detected}"
echo "Common Name: $COMMON_NAME"
echo "Country: $COUNTRY"
echo "State: $STATE"
echo "Locality: $LOCALITY"
echo "Organization: $ORG"
echo "Org Unit: $OU"
echo "Key Length: $KEY_BITS bits"
if [ -n "$ADDITIONAL_DNS" ]; then
echo "Additional DNS: $ADDITIONAL_DNS"
fi
echo "Output file: $OUTPUT_FILE"
echo "=========================================="
echo ""
# Build SAN entries
SAN_DNS="DNS.1 = $COMMON_NAME
DNS.2 = $SHORT_NAME"
DNS_COUNTER=3
# Add alternative names if common name contains domain
if [[ "$COMMON_NAME" == *.* ]]; then
SAN_DNS="$SAN_DNS
DNS.$DNS_COUNTER = ${SHORT_NAME}.${COMMON_NAME#*.}"
((DNS_COUNTER++))
fi
# Add additional DNS names if provided
if [ -n "$ADDITIONAL_DNS" ]; then
IFS=',' read -ra EXTRA_DNS <<< "$ADDITIONAL_DNS"
for dns in "${EXTRA_DNS[@]}"; do
# Trim whitespace
dns=$(echo "$dns" | xargs)
if [ -n "$dns" ]; then
SAN_DNS="$SAN_DNS
DNS.$DNS_COUNTER = $dns"
((DNS_COUNTER++))
fi
done
fi
# Add IP address if detected
SAN_IP=""
if [ -n "$TARGET_IP" ]; then
SAN_IP="IP.1 = $TARGET_IP"
fi
# Create OpenSSL config
CONFIG_CONTENT="[req]
default_bits = $KEY_BITS
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C=$COUNTRY
ST=$STATE
L=$LOCALITY
O=$ORG
OU=$OU
CN=$COMMON_NAME
[v3_req]
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
$SAN_DNS"
# Append IP if available
if [ -n "$SAN_IP" ]; then
CONFIG_CONTENT="$CONFIG_CONTENT
$SAN_IP"
fi
echo "[1/4] Creating OpenSSL configuration..."
echo "$CONFIG_CONTENT" > /tmp/csr_config.conf
echo "[2/4] Copying config to target host..."
scp /tmp/csr_config.conf ${SSH_USER}@${TARGET_HOST}:/tmp/csr_config.conf
if [ $? -ne 0 ]; then
echo "Error: Failed to copy config to target host"
exit 1
fi
echo "[3/4] Generating $KEY_BITS-bit RSA key and CSR on target host..."
ssh ${SSH_USER}@${TARGET_HOST} "openssl req -new -newkey rsa:$KEY_BITS -nodes -keyout /tmp/${SHORT_NAME}.key -out /tmp/${SHORT_NAME}.csr -config /tmp/csr_config.conf"
if [ $? -ne 0 ]; then
echo "Error: Failed to generate CSR on target host"
exit 1
fi
echo "[4/4] Downloading CSR..."
scp ${SSH_USER}@${TARGET_HOST}:/tmp/${SHORT_NAME}.csr "$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo "Error: Failed to download CSR"
exit 1
fi
# Clean up local temp file
rm -f /tmp/csr_config.conf
echo ""
echo "=========================================="
echo "✓ CSR generated successfully!"
echo "=========================================="
echo "Certificate request saved to: $OUTPUT_FILE"
echo ""
echo "CSR details:"
openssl req -in "$OUTPUT_FILE" -noout -text | grep -A 10 "Subject:"
echo ""
echo "Subject Alternative Names:"
openssl req -in "$OUTPUT_FILE" -noout -text | grep -A 20 "Subject Alternative Name" || echo " (none found)"
echo ""
echo "Key details:"
openssl req -in "$OUTPUT_FILE" -noout -text | grep "Public-Key:"
echo ""
echo "IMPORTANT: Private key is stored on target host at:"
echo " /tmp/${SHORT_NAME}.key"
echo ""
echo "Next step: Sign this CSR with:"
echo " ./sign-cert.sh $OUTPUT_FILE $SHORT_NAME"
echo "=========================================="

164
generate-csr-local.sh Executable file
View File

@@ -0,0 +1,164 @@
#!/bin/bash
# Script to generate a certificate request locally (for systems without SCP/SFTP)
# Usage: ./generate-csr-local.sh <common-name> [country] [state] [locality] [org] [ou] [key-bits] [additional-dns] [ip-address]
set -e
# Check arguments
if [ $# -lt 1 ]; then
echo "Usage: $0 <common-name> [country] [state] [locality] [org] [ou] [key-bits] [additional-dns] [ip-address]"
echo ""
echo "Example: $0 srv-wmw-ha01.egonetix.lan DE berlin berlin egonetix it 4096 '' 172.20.70.10"
exit 1
fi
COMMON_NAME="$1"
COUNTRY="${2:-DE}"
STATE="${3:-berlin}"
LOCALITY="${4:-berlin}"
ORG="${5:-egonetix}"
OU="${6:-it}"
KEY_BITS="${7:-4096}"
ADDITIONAL_DNS="${8:-}"
IP_ADDRESS="${9:-}"
# Extract short hostname from common name
SHORT_NAME=$(echo "$COMMON_NAME" | cut -d'.' -f1)
OUTPUT_REQ="${SHORT_NAME}.req"
OUTPUT_KEY="${SHORT_NAME}.key"
OUTPUT_CSR="${SHORT_NAME}.csr"
echo "=========================================="
echo "Local Certificate Request Generation"
echo "=========================================="
echo "Common Name: $COMMON_NAME"
echo "Country: $COUNTRY"
echo "State: $STATE"
echo "Locality: $LOCALITY"
echo "Organization: $ORG"
echo "Org Unit: $OU"
echo "Key Length: $KEY_BITS bits"
if [ -n "$ADDITIONAL_DNS" ]; then
echo "Additional DNS: $ADDITIONAL_DNS"
fi
if [ -n "$IP_ADDRESS" ]; then
echo "IP Address: $IP_ADDRESS"
fi
echo "Output files: $OUTPUT_REQ, $OUTPUT_KEY"
echo "=========================================="
echo ""
# Build SAN entries
SAN_DNS="DNS.1 = $COMMON_NAME
DNS.2 = $SHORT_NAME"
DNS_COUNTER=3
# Add alternative names if common name contains domain
if [[ "$COMMON_NAME" == *.* ]]; then
SAN_DNS="$SAN_DNS
DNS.$DNS_COUNTER = ${SHORT_NAME}.${COMMON_NAME#*.}"
((DNS_COUNTER++))
fi
# Add additional DNS names if provided
if [ -n "$ADDITIONAL_DNS" ]; then
IFS=',' read -ra EXTRA_DNS <<< "$ADDITIONAL_DNS"
for dns in "${EXTRA_DNS[@]}"; do
# Trim whitespace
dns=$(echo "$dns" | xargs)
if [ -n "$dns" ]; then
SAN_DNS="$SAN_DNS
DNS.$DNS_COUNTER = $dns"
((DNS_COUNTER++))
fi
done
fi
# Add IP addresses if provided (comma-separated)
SAN_IP=""
if [ -n "$IP_ADDRESS" ]; then
IP_COUNTER=1
IFS=',' read -ra IP_ADDRS <<< "$IP_ADDRESS"
for ip in "${IP_ADDRS[@]}"; do
# Trim whitespace
ip=$(echo "$ip" | xargs)
if [ -n "$ip" ]; then
if [ -z "$SAN_IP" ]; then
SAN_IP="IP.$IP_COUNTER = $ip"
else
SAN_IP="$SAN_IP
IP.$IP_COUNTER = $ip"
fi
((IP_COUNTER++))
fi
done
fi
# Create OpenSSL config
CONFIG_CONTENT="[req]
default_bits = $KEY_BITS
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[dn]
C=$COUNTRY
ST=$STATE
L=$LOCALITY
O=$ORG
OU=$OU
CN=$COMMON_NAME
[v3_req]
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
$SAN_DNS"
# Append IP if available
if [ -n "$SAN_IP" ]; then
CONFIG_CONTENT="$CONFIG_CONTENT
$SAN_IP"
fi
CONFIG_FILE="/tmp/csr_config_${SHORT_NAME}.conf"
echo "[1/2] Creating OpenSSL configuration..."
echo "$CONFIG_CONTENT" > "$CONFIG_FILE"
echo "[2/2] Generating $KEY_BITS-bit RSA key and CSR locally..."
openssl req -new -newkey rsa:$KEY_BITS -nodes -keyout "$OUTPUT_KEY" -out "$OUTPUT_CSR" -config "$CONFIG_FILE"
# Also create the .req file for consistency with other scripts
cp "$OUTPUT_CSR" "$OUTPUT_REQ"
# Clean up config file
rm -f "$CONFIG_FILE"
# Set proper permissions on private key
chmod 600 "$OUTPUT_KEY"
echo ""
echo "=========================================="
echo "✓ Certificate files generated locally!"
echo "=========================================="
echo "Certificate request: $OUTPUT_REQ"
echo "Private key: $OUTPUT_KEY"
echo ""
echo "CSR details:"
openssl req -in "$OUTPUT_REQ" -noout -text | grep -A 10 "Subject:"
echo ""
echo "Subject Alternative Names:"
openssl req -in "$OUTPUT_REQ" -noout -text | grep -A 20 "Subject Alternative Name" || echo " (none found)"
echo ""
echo "Key details:"
openssl req -in "$OUTPUT_REQ" -noout -text | grep "Public-Key:"
echo ""
echo "⚠️ IMPORTANT: Keep $OUTPUT_KEY secure!"
echo ""
echo "Next step: Sign this CSR with:"
echo " ./sign-cert.sh $OUTPUT_REQ $SHORT_NAME"
echo "=========================================="

View File

@@ -21,6 +21,17 @@ ORG="${6:-egonetix}"
OU="${7:-it}"
KEY_BITS="${8:-4096}"
ADDITIONAL_DNS="${9:-}"
SSH_USER="${SSH_USER:-root}"
SSH_PASSWORD="${SSH_PASSWORD:-}"
# Setup SSH/SCP commands with password support
if [ -n "$SSH_PASSWORD" ] && command -v sshpass >/dev/null 2>&1; then
SSH_CMD="sshpass -p '$SSH_PASSWORD' ssh"
SCP_CMD="sshpass -p '$SSH_PASSWORD' scp"
else
SSH_CMD="ssh"
SCP_CMD="scp"
fi
# Extract short hostname from common name
SHORT_NAME=$(echo "$COMMON_NAME" | cut -d'.' -f1)
@@ -31,7 +42,7 @@ if [[ "$TARGET_HOST" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TARGET_IP="$TARGET_HOST"
else
# Try to resolve hostname to IP
TARGET_IP=$(ssh -o ConnectTimeout=5 -o BatchMode=yes root@${TARGET_HOST} "hostname -I | awk '{print \$1}'" 2>/dev/null || echo "")
TARGET_IP=$($SSH_CMD -o ConnectTimeout=5 ${SSH_USER}@${TARGET_HOST} "hostname -I | awk '{print \$1}'" 2>/dev/null || echo "")
if [ -z "$TARGET_IP" ]; then
# Fallback: try local resolution
TARGET_IP=$(getent hosts "$TARGET_HOST" 2>/dev/null | awk '{print $1}' | head -1 || echo "")
@@ -124,21 +135,24 @@ echo "[1/4] Creating OpenSSL configuration..."
echo "$CONFIG_CONTENT" > /tmp/csr_config.conf
echo "[2/4] Copying config to target host..."
scp /tmp/csr_config.conf root@${TARGET_HOST}:/tmp/csr_config.conf
sleep 0.5 # Avoid SSH rate limiting
$SCP_CMD /tmp/csr_config.conf ${SSH_USER}@${TARGET_HOST}:/tmp/csr_config.conf
if [ $? -ne 0 ]; then
echo "Error: Failed to copy config to target host"
exit 1
fi
echo "[3/4] Generating $KEY_BITS-bit RSA key and CSR on target host..."
ssh root@${TARGET_HOST} "openssl req -new -newkey rsa:$KEY_BITS -nodes -keyout /tmp/${SHORT_NAME}.key -out /tmp/${SHORT_NAME}.csr -config /tmp/csr_config.conf"
sleep 0.5 # Avoid SSH rate limiting
$SSH_CMD ${SSH_USER}@${TARGET_HOST} "openssl req -new -newkey rsa:$KEY_BITS -nodes -keyout /tmp/${SHORT_NAME}.key -out /tmp/${SHORT_NAME}.csr -config /tmp/csr_config.conf"
if [ $? -ne 0 ]; then
echo "Error: Failed to generate CSR on target host"
exit 1
fi
echo "[4/4] Downloading CSR..."
scp root@${TARGET_HOST}:/tmp/${SHORT_NAME}.csr "$OUTPUT_FILE"
sleep 0.5 # Avoid SSH rate limiting
$SCP_CMD ${SSH_USER}@${TARGET_HOST}:/tmp/${SHORT_NAME}.csr "$OUTPUT_FILE"
if [ $? -ne 0 ]; then
echo "Error: Failed to download CSR"
exit 1