Last active
March 5, 2025 09:38
-
-
Save bogorad/7207247ec22727466d1e5d4fac3e73c3 to your computer and use it in GitHub Desktop.
generate-host-key.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -euo pipefail | |
# Function to log error and exit | |
die() { | |
echo "ERROR: $1" >&2 | |
exit 1 | |
} | |
# Function for logging with timestamps | |
log() { | |
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | |
} | |
# Function to show usage | |
usage() { | |
cat << EOF | |
Usage: $0 [OPTIONS] HOSTNAME [HOSTNAME...] | |
Options: | |
-h, --help Show this help message | |
Arguments: | |
HOSTNAME One or more hostnames to process | |
EOF | |
exit 1 | |
} | |
# Function to validate Bitwarden login status | |
check_rbw_status() { | |
rbw unlock || die "Failed to login to Bitwarden, try manually via 'rbw login'" | |
} | |
# Function to cleanup temporary files | |
cleanup() { | |
log "Cleaning up temporary files..." | |
if [[ -d "$TEMP_DIR" ]]; then | |
rm -rf "$TEMP_DIR" | |
log "Removed temporary directory: $TEMP_DIR" | |
fi | |
} | |
# Function to verify sops.yaml syntax | |
verify_sops_yaml() { | |
local yaml_file="$1" | |
if ! sops -v "$yaml_file" &>/dev/null; then | |
die "YAML validation failed for $yaml_file" | |
fi | |
log "YAML validation successful" | |
} | |
# Function to backup existing configuration | |
backup_config() { | |
local file="$1" | |
if [[ -f "$file" ]]; then | |
local backup | |
backup="${file}.backup-$(date +'%Y%m%d-%H%M%S')" | |
cp "$file" "$backup" | |
log "Backup created: $backup" | |
fi | |
} | |
# Function to process a single host | |
process_host() { | |
local host="$1" | |
local key_dir="$TEMP_DIR/$host" | |
local key_name="${KEY_PREFIX}${host}" | |
local fake_editor="/tmp/fake-editor.sh" | |
log "Processing host: $host" | |
# Validate hostname | |
[[ $host =~ ^[a-zA-Z0-9-]+$ ]] || | |
die "Invalid hostname $host. Use only letters, numbers, and hyphens." | |
# Check for existing configurations | |
if [[ -f "$SOPS_YAML" ]] && grep -q "&$host" "$SOPS_YAML" 2>/dev/null; then | |
die "Host $host already exists in $SOPS_YAML" | |
fi | |
rbw get "$key_name" &>/dev/null && | |
die "Key '$key_name' already exists in Bitwarden" | |
# Create directory | |
mkdir -p "$key_dir" || | |
die "Failed to create directory $key_dir" | |
# Generate SSH key | |
log "Generating ED25519 SSH key..." | |
ssh-keygen -t ed25519 -N "" -f "$key_dir/ssh_host_ed25519_key" -q || | |
die "Failed to generate SSH key" | |
# Store private key in Bitwarden | |
log "Storing private key in Bitwarden..." | |
# cleanup fake editor | |
rm -rf "$fake_editor" | |
# create fake-editor.sh | |
cat <<EOL > "$fake_editor" | |
#!/usr/bin/env bash | |
echo "no-password" >\$1 | |
cat "$key_dir/ssh_host_ed25519_key" >>\$1 | |
EOL | |
# make it executable | |
chmod +x "$fake_editor" | |
# now run rbw that will call our fake-editor | |
EDITOR="$fake_editor" rbw add "$key_name" || | |
die "Failed to store key in Bitwarden" | |
# Convert SSH key to age format | |
log "Converting SSH key to age format..." | |
age_key=$(ssh-to-age < "$key_dir/ssh_host_ed25519_key.pub") || | |
die "Failed to convert SSH key to age format" | |
# Update .sops.yaml | |
if [[ ! -f "$SOPS_YAML" ]]; then | |
log "Creating new $SOPS_YAML file..." | |
cat > "$SOPS_YAML" << EOF | |
keys: | |
- &$host $age_key # Added $(date +'%Y-%m-%d') | |
creation_rules: | |
- path_regex: secrets/[^/]+\.yaml$ | |
key_groups: | |
- age: | |
- *$host | |
EOF | |
else | |
log "Updating existing $SOPS_YAML file..." | |
sed -i "/^keys:/a\\ - \&$host $age_key # Added $(date +'%Y-%m-%d')" "$SOPS_YAML" | |
sed -i "/age:/a\\ - *$host" "$SOPS_YAML" | |
fi | |
log "Completed processing for host: $host" | |
} | |
# Parse command line arguments | |
HOSTS=() | |
while [[ $# -gt 0 ]]; do | |
case $1 in | |
-h|--help) | |
usage | |
;; | |
-*) | |
die "Unknown option: $1" | |
;; | |
*) | |
HOSTS+=("$1") | |
shift | |
;; | |
esac | |
done | |
# Check if we have any hosts to process | |
[[ ${#HOSTS[@]} -eq 0 ]] && usage | |
# Set variables | |
KEY_PREFIX="ssh-host-key-" | |
SOPS_YAML="../.sops.yaml" | |
# Create secure temporary directory | |
TEMP_DIR=$(mktemp -d) || die "Failed to create temporary directory" | |
chmod 700 "$TEMP_DIR" | |
# Check for required tools | |
for cmd in ssh-to-age rbw ssh-keygen sops; do | |
command -v "$cmd" &> /dev/null || | |
die "$cmd not found. Please install it first." | |
done | |
# Check Bitwarden login status | |
check_rbw_status | |
# Setup cleanup trap | |
trap cleanup EXIT | |
# Backup existing configuration | |
backup_config "$SOPS_YAML" | |
# Process each host | |
for host in "${HOSTS[@]}"; do | |
process_host "$host" | |
done | |
# Verify the YAML | |
verify_sops_yaml "$SOPS_YAML" | |
# sync rbw just in case | |
rbw sync | |
# update keys in sops secrets | |
[[ -f "../secrets/secrets.yaml" ]] && sops updatekeys secrets/secrets.yaml | |
log "Successfully completed all operations" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment