Extend functionality

This commit is contained in:
Ruben Solvang 2025-03-03 12:37:55 +01:00
parent 5a0330f70d
commit 2b07e1266c
2 changed files with 210 additions and 113 deletions

View file

@ -1,15 +1,18 @@
# Certwarden Certificate Management # Certwarden Certificate Management
A bash script for managing SSL/TLS certificates through the Certwarden API. This tool provides a simple interface for downloading, installing, and managing certificates on your system. A bash script for managing SSL/TLS certificates through the Certwarden API. This tool provides both automated and interactive interfaces for downloading, installing, and managing certificates on your system.
## Features ## Features
- Download certificates and private keys from Certwarden server - Download and verify certificates and private keys from Certwarden server
- Automatic installation with proper permissions - Automatic installation with proper permissions and ownership
- List installed certificates - Certificate and key pair validation
- Check certificate expiration dates - Service reload after certificate updates
- Certificate expiration monitoring
- Interactive menu-driven interface - Interactive menu-driven interface
- Automated mode support through environment variables - Automated mode support through environment configuration
- Proper error handling and logging
- Support for multiple certificates
## Prerequisites ## Prerequisites
@ -31,28 +34,79 @@ cd certman
chmod +x certman.sh chmod +x certman.sh
``` ```
3. Create a `.env` file with your configuration: 3. Create a `.env` file with your configuration:
```bash ```bash
CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net" # Server Configuration
API_KEY="" CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443"
CERT_NAME="$(hostname).crt" # defaults to hostname # Certificate Paths
CERT_PATH="/etc/ssl/certs" CERT_PATH="/etc/forgejo"
KEY_PATH="/etc/ssl/private" KEY_PATH="/etc/forgejo"
TEMP_PATH="/tmp/certman"
# Service Configuration
SERVICE_NAME="forgejo"
CERT_OWNER="git"
CERT_GROUP="git"
CERT_PERMISSIONS="644"
KEY_PERMISSIONS="600"
# Certificate Configurations (JSON format)
CERTIFICATES='[
{
"domain": "example.com",
"cert_api_key": "your_cert_api_key",
"key_api_key": "your_key_api_key"
}
]'
# Optional: Auto mode configuration
AUTO_MODE="false" AUTO_MODE="false"
TEMP_PATH="/tmp/cert_temp"
``` ```
### Environment Variables Explained ### Environment Variables Explained
| Variable | Description | Default Value | Required | | Variable | Description | Required |
|----------|-------------|---------------|----------| |----------|-------------|----------|
| CERTWARDEN_SERVER | Certwarden API server hostname | certwarden.dmz.skyfritt.net | Yes | | CERTWARDEN_SERVER | Certwarden API server hostname and port | Yes |
| API_KEY | Your Certwarden API key | Empty | Yes for auto mode | | CERT_PATH | Directory for certificate storage | Yes |
| CERT_NAME | Certificate name to manage | $(hostname).crt | Yes | | KEY_PATH | Directory for private key storage | Yes |
| CERT_PATH | Directory for certificate storage | /etc/ssl/certs | Yes | | TEMP_PATH | Temporary directory for downloads | Yes |
| KEY_PATH | Directory for private key storage | /etc/ssl/private | Yes | | SERVICE_NAME | Service to reload after certificate updates | Yes |
| AUTO_MODE | Enable automated operation | false | No | | CERT_OWNER | User owner for certificate files | Yes |
| TEMP_PATH | Temporary directory for downloads | /tmp/cert_temp | Yes | | CERT_GROUP | Group owner for certificate files | Yes |
| CERT_PERMISSIONS | Certificate file permissions | Yes |
| KEY_PERMISSIONS | Private key file permissions | Yes |
| CERTIFICATES | JSON array of certificate configurations | Yes |
| AUTO_MODE | Enable automated operation | No |
## Usage
### Interactive Mode
Run the script without any arguments:
```bash
./certman.sh
```
This will present a menu with the following options:
1. Process all certificates
2. List installed certificates
3. Check certificate expiration
4. Exit
### Automated Mode
Set `AUTO_MODE="true"` in the `.env` file and run the script. This is suitable for cron jobs.
### Cron Configuration
Add these lines to your crontab for automated certificate management:
```cron
@reboot sleep 15 && /path/to/certman.sh
5 4 * * 2 /path/to/certman.sh
```
## Security Considerations
- Store the script and `.env` file in a secure location with restricted permissions
- Use appropriate permissions for certificate and key files
- Keep API keys secure and rotate them periodically
- Run the script as a user with appropriate privileges

View file

@ -1,22 +1,31 @@
#!/bin/bash #!/bin/bash
# Exit on any error, treat unset variables as errors, and propagate errors in pipelines
set -euo pipefail
# Load environment variables
if [ -f .env ]; then if [ -f .env ]; then
source .env source .env
else else
echo "No .env file found." echo "No .env file found."
exit 1
fi fi
# Color definitions
GREEN='\033[0;32m' GREEN='\033[0;32m'
RED='\033[0;31m' RED='\033[0;31m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Temporary directory for certs
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT
check_requirements() { check_requirements() {
local required_commands=("curl" "jq") local required_commands=("curl" "jq" "openssl")
for cmd in "${required_commands[@]}"; do for cmd in "${required_commands[@]}"; do
if ! command -v "$cmd" &> /dev/null; then if ! command -v "$cmd" &> /dev/null; then
echo -e "${RED}Error: Required command '$cmd' is not installed.${NC}" echo -e "${RED}Error: Required command '$cmd' is not installed.${NC}"
echo "Please install it using your package manager."
exit 1 exit 1
fi fi
done done
@ -24,42 +33,59 @@ check_requirements() {
validate_api_key() { validate_api_key() {
local api_key=$1 local api_key=$1
if [[ ! $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]; then [[ $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]
return 1
fi
return 0
} }
setup_directories() { setup_directories() {
if ! mkdir -p "$CERT_PATH" "$KEY_PATH" "$TEMP_PATH"; then local dirs=("$CERT_PATH" "$KEY_PATH" "$TEMP_PATH")
echo -e "${RED}Error: Failed to create required directories${NC}" for dir in "${dirs[@]}"; do
exit 1 if ! mkdir -p "$dir"; then
fi echo -e "${RED}Error: Failed to create directory: $dir${NC}"
exit 1
fi
done
} }
download_certificate() { download_and_verify_cert() {
local cert_name=$1 local domain=$1
local api_key=$2 local cert_api_key=$2
local key_api_key=$3
local temp_cert="$TEMP_DIR/$domain.crt"
local temp_key="$TEMP_DIR/$domain.key"
echo -e "${BLUE}Downloading certificate for $cert_name...${NC}" echo -e "${BLUE}Processing certificate for $domain${NC}"
# Download certificate # Download certificate
if curl -fL -o "$TEMP_PATH/$cert_name.crt" \ if ! curl -fL -o "$temp_cert" -H "X-API-Key: $cert_api_key" \
-H "X-API-Key: $api_key" \ "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$domain"; then
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$cert_name"; then echo -e "${RED}Failed to download certificate for $domain${NC}"
echo -e "${GREEN}Certificate downloaded successfully${NC}"
else
echo -e "${RED}Failed to download certificate${NC}"
return 1 return 1
fi fi
# Download private key # Download private key
if curl -fL -o "$TEMP_PATH/$cert_name.key" \ if ! curl -fL -o "$temp_key" -H "X-API-Key: $key_api_key" \
-H "X-API-Key: $api_key" \ "https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$domain"; then
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$cert_name"; then echo -e "${RED}Failed to download private key for $domain${NC}"
echo -e "${GREEN}Private key downloaded successfully${NC}" return 1
else fi
echo -e "${RED}Failed to download private key${NC}"
# Verify files are not empty
if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ]; then
echo -e "${RED}Downloaded files are empty for $domain${NC}"
return 1
fi
# Validate certificate and key match
local cert_fingerprint
cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey |
openssl pkey -pubin -outform DER 2>/dev/null |
openssl dgst -sha256)
local key_fingerprint
key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null |
openssl dgst -sha256)
if [ "$cert_fingerprint" != "$key_fingerprint" ]; then
echo -e "${RED}Certificate and key do not match for $domain${NC}"
return 1 return 1
fi fi
@ -67,41 +93,93 @@ download_certificate() {
} }
install_certificate() { install_certificate() {
local cert_name=$1 local domain=$1
local final_cert="$CERT_PATH/$domain.crt"
local final_key="$KEY_PATH/$domain.key"
local temp_cert="$TEMP_DIR/$domain.crt"
local temp_key="$TEMP_DIR/$domain.key"
local needs_reload=0
# Move files to their final locations # Check if certificate needs updating
if sudo mv "$TEMP_PATH/$cert_name.crt" "$CERT_PATH/" && \ if [ -f "$final_cert" ]; then
sudo mv "$TEMP_PATH/$cert_name.key" "$KEY_PATH/"; then if ! cmp -s "$final_cert" "$temp_cert"; then
echo -e "${GREEN}Certificate and key installed successfully${NC}" needs_reload=1
fi
# Set appropriate permissions
sudo chmod 644 "$CERT_PATH/$cert_name.crt"
sudo chmod 600 "$KEY_PATH/$cert_name.key"
return 0
else else
echo -e "${RED}Failed to install certificate and key${NC}" needs_reload=1
return 1 fi
# Install new certificate and key
if [ $needs_reload -eq 1 ]; then
if ! cp -f "$temp_cert" "$final_cert" || ! cp -f "$temp_key" "$final_key"; then
echo -e "${RED}Failed to install certificate files for $domain${NC}"
return 1
fi
# Set permissions and ownership
if ! chown "$CERT_OWNER:$CERT_GROUP" "$final_cert" "$final_key" || \
! chmod "$CERT_PERMISSIONS" "$final_cert" || \
! chmod "$KEY_PERMISSIONS" "$final_key"; then
echo -e "${RED}Failed to set permissions for $domain${NC}"
return 1
fi
echo -e "${GREEN}Certificate updated for $domain${NC}"
return 0
fi
echo -e "${BLUE}Certificate already up to date for $domain${NC}"
return 2
}
process_certificates() {
local service_reloaded=0
local certs_array
certs_array=$(echo "$CERTIFICATES" | jq -c '.[]')
while IFS= read -r cert; do
local domain=$(echo "$cert" | jq -r '.domain')
local cert_api_key=$(echo "$cert" | jq -r '.cert_api_key')
local key_api_key=$(echo "$cert" | jq -r '.key_api_key')
if download_and_verify_cert "$domain" "$cert_api_key" "$key_api_key"; then
if install_certificate "$domain"; then
service_reloaded=1
fi
fi
done <<< "$certs_array"
# Reload service if needed
if [ $service_reloaded -eq 1 ]; then
echo -e "${BLUE}Reloading $SERVICE_NAME service...${NC}"
if systemctl reload "$SERVICE_NAME"; then
echo -e "${GREEN}Service reloaded successfully${NC}"
else
echo -e "${RED}Failed to reload service${NC}"
return 1
fi
fi fi
} }
# Main menu function check_cert_expiration() {
main_menu() { echo -e "\n${BLUE}Certificate Expiration Dates:${NC}"
# If in auto mode and we have cert name and API key, process automatically for cert in "$CERT_PATH"/*.crt; do
if [ "$AUTO_MODE" = "true" ] && [ -n "$CERT_NAME" ] && [ -n "$API_KEY" ]; then if [ -f "$cert" ]; then
if validate_api_key "$API_KEY"; then echo -n "$(basename "$cert"): "
if download_certificate "$CERT_NAME" "$API_KEY"; then openssl x509 -enddate -noout -in "$cert"
install_certificate "$CERT_NAME"
fi
else
echo -e "${RED}Invalid API key format in .env file${NC}"
fi fi
done
}
main_menu() {
if [ "$AUTO_MODE" = "true" ]; then
process_certificates
exit 0 exit 0
fi fi
while true; do while true; do
echo -e "\n${BLUE}Certwarden Certificate Management${NC}" echo -e "\n${BLUE}Certificate Management Menu${NC}"
echo "1. Download and install new certificate" echo "1. Process all certificates"
echo "2. List installed certificates" echo "2. List installed certificates"
echo "3. Check certificate expiration" echo "3. Check certificate expiration"
echo "4. Exit" echo "4. Exit"
@ -109,43 +187,11 @@ main_menu() {
read -r -p "Select an option (1-4): " choice read -r -p "Select an option (1-4): " choice
case $choice in case $choice in
1) 1) process_certificates ;;
if [ -z "$CERT_NAME" ]; then 2) ls -l "$CERT_PATH"/*.crt 2>/dev/null || echo "No certificates found" ;;
read -r -p "Enter certificate name: " CERT_NAME 3) check_cert_expiration ;;
fi 4) echo -e "${GREEN}Exiting...${NC}"; exit 0 ;;
if [ -z "$API_KEY" ]; then *) echo -e "${RED}Invalid option${NC}" ;;
read -r -p "Enter API key: " API_KEY
fi
if ! validate_api_key "$API_KEY"; then
echo -e "${RED}Invalid API key format${NC}"
continue
fi
if download_certificate "$CERT_NAME" "$API_KEY"; then
install_certificate "$CERT_NAME"
fi
;;
2)
echo -e "\n${BLUE}Installed Certificates:${NC}"
ls -l "$CERT_PATH"/*.crt 2>/dev/null || echo "No certificates found"
;;
3)
echo -e "\n${BLUE}Certificate Expiration Dates:${NC}"
for cert in "$CERT_PATH"/*.crt; do
if [ -f "$cert" ]; then
echo -n "$(basename "$cert"): "
openssl x509 -enddate -noout -in "$cert"
fi
done
;;
4)
echo -e "${GREEN}Exiting...${NC}"
exit 0
;;
*)
echo -e "${RED}Invalid option${NC}"
;;
esac esac
done done
} }
@ -153,7 +199,4 @@ main_menu() {
# Script initialization # Script initialization
check_requirements check_requirements
setup_directories setup_directories
# Start the script
echo -e "${GREEN}Welcome to Certwarden Certificate Management${NC}"
main_menu main_menu