Extend functionality
This commit is contained in:
parent
5a0330f70d
commit
2b07e1266c
2 changed files with 210 additions and 113 deletions
98
README.md
98
README.md
|
@ -1,15 +1,18 @@
|
|||
# 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
|
||||
|
||||
- Download certificates and private keys from Certwarden server
|
||||
- Automatic installation with proper permissions
|
||||
- List installed certificates
|
||||
- Check certificate expiration dates
|
||||
- Download and verify certificates and private keys from Certwarden server
|
||||
- Automatic installation with proper permissions and ownership
|
||||
- Certificate and key pair validation
|
||||
- Service reload after certificate updates
|
||||
- Certificate expiration monitoring
|
||||
- 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
|
||||
|
||||
|
@ -31,28 +34,79 @@ cd certman
|
|||
chmod +x certman.sh
|
||||
```
|
||||
|
||||
|
||||
3. Create a `.env` file with your configuration:
|
||||
```bash
|
||||
CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net"
|
||||
API_KEY=""
|
||||
# Server Configuration
|
||||
CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443"
|
||||
|
||||
CERT_NAME="$(hostname).crt" # defaults to hostname
|
||||
CERT_PATH="/etc/ssl/certs"
|
||||
KEY_PATH="/etc/ssl/private"
|
||||
# Certificate Paths
|
||||
CERT_PATH="/etc/forgejo"
|
||||
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"
|
||||
TEMP_PATH="/tmp/cert_temp"
|
||||
```
|
||||
|
||||
### Environment Variables Explained
|
||||
|
||||
| Variable | Description | Default Value | Required |
|
||||
|----------|-------------|---------------|----------|
|
||||
| CERTWARDEN_SERVER | Certwarden API server hostname | certwarden.dmz.skyfritt.net | Yes |
|
||||
| API_KEY | Your Certwarden API key | Empty | Yes for auto mode |
|
||||
| CERT_NAME | Certificate name to manage | $(hostname).crt | Yes |
|
||||
| CERT_PATH | Directory for certificate storage | /etc/ssl/certs | Yes |
|
||||
| KEY_PATH | Directory for private key storage | /etc/ssl/private | Yes |
|
||||
| AUTO_MODE | Enable automated operation | false | No |
|
||||
| TEMP_PATH | Temporary directory for downloads | /tmp/cert_temp | Yes |
|
||||
| Variable | Description | Required |
|
||||
|----------|-------------|----------|
|
||||
| CERTWARDEN_SERVER | Certwarden API server hostname and port | Yes |
|
||||
| CERT_PATH | Directory for certificate storage | Yes |
|
||||
| KEY_PATH | Directory for private key storage | Yes |
|
||||
| TEMP_PATH | Temporary directory for downloads | Yes |
|
||||
| SERVICE_NAME | Service to reload after certificate updates | Yes |
|
||||
| CERT_OWNER | User owner for certificate files | 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
|
||||
|
|
227
certman.sh
227
certman.sh
|
@ -1,22 +1,31 @@
|
|||
#!/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
|
||||
source .env
|
||||
else
|
||||
echo "No .env file found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Color definitions
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Temporary directory for certs
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TEMP_DIR"' EXIT
|
||||
|
||||
check_requirements() {
|
||||
local required_commands=("curl" "jq")
|
||||
local required_commands=("curl" "jq" "openssl")
|
||||
for cmd in "${required_commands[@]}"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
echo -e "${RED}Error: Required command '$cmd' is not installed.${NC}"
|
||||
echo "Please install it using your package manager."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
@ -24,42 +33,59 @@ check_requirements() {
|
|||
|
||||
validate_api_key() {
|
||||
local api_key=$1
|
||||
if [[ ! $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
[[ $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]
|
||||
}
|
||||
|
||||
setup_directories() {
|
||||
if ! mkdir -p "$CERT_PATH" "$KEY_PATH" "$TEMP_PATH"; then
|
||||
echo -e "${RED}Error: Failed to create required directories${NC}"
|
||||
local dirs=("$CERT_PATH" "$KEY_PATH" "$TEMP_PATH")
|
||||
for dir in "${dirs[@]}"; do
|
||||
if ! mkdir -p "$dir"; then
|
||||
echo -e "${RED}Error: Failed to create directory: $dir${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
download_certificate() {
|
||||
local cert_name=$1
|
||||
local api_key=$2
|
||||
download_and_verify_cert() {
|
||||
local domain=$1
|
||||
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
|
||||
if curl -fL -o "$TEMP_PATH/$cert_name.crt" \
|
||||
-H "X-API-Key: $api_key" \
|
||||
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$cert_name"; then
|
||||
echo -e "${GREEN}Certificate downloaded successfully${NC}"
|
||||
else
|
||||
echo -e "${RED}Failed to download certificate${NC}"
|
||||
if ! curl -fL -o "$temp_cert" -H "X-API-Key: $cert_api_key" \
|
||||
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$domain"; then
|
||||
echo -e "${RED}Failed to download certificate for $domain${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Download private key
|
||||
if curl -fL -o "$TEMP_PATH/$cert_name.key" \
|
||||
-H "X-API-Key: $api_key" \
|
||||
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$cert_name"; then
|
||||
echo -e "${GREEN}Private key downloaded successfully${NC}"
|
||||
else
|
||||
echo -e "${RED}Failed to download private key${NC}"
|
||||
if ! curl -fL -o "$temp_key" -H "X-API-Key: $key_api_key" \
|
||||
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$domain"; then
|
||||
echo -e "${RED}Failed to download private key for $domain${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
fi
|
||||
|
||||
|
@ -67,70 +93,75 @@ download_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
|
||||
if sudo mv "$TEMP_PATH/$cert_name.crt" "$CERT_PATH/" && \
|
||||
sudo mv "$TEMP_PATH/$cert_name.key" "$KEY_PATH/"; then
|
||||
echo -e "${GREEN}Certificate and key installed successfully${NC}"
|
||||
|
||||
# Set appropriate permissions
|
||||
sudo chmod 644 "$CERT_PATH/$cert_name.crt"
|
||||
sudo chmod 600 "$KEY_PATH/$cert_name.key"
|
||||
|
||||
return 0
|
||||
# Check if certificate needs updating
|
||||
if [ -f "$final_cert" ]; then
|
||||
if ! cmp -s "$final_cert" "$temp_cert"; then
|
||||
needs_reload=1
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}Failed to install certificate and key${NC}"
|
||||
needs_reload=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
|
||||
}
|
||||
|
||||
# Main menu function
|
||||
main_menu() {
|
||||
# If in auto mode and we have cert name and API key, process automatically
|
||||
if [ "$AUTO_MODE" = "true" ] && [ -n "$CERT_NAME" ] && [ -n "$API_KEY" ]; then
|
||||
if validate_api_key "$API_KEY"; then
|
||||
if download_certificate "$CERT_NAME" "$API_KEY"; then
|
||||
install_certificate "$CERT_NAME"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}Invalid API key format in .env file${NC}"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while true; do
|
||||
echo -e "\n${BLUE}Certwarden Certificate Management${NC}"
|
||||
echo "1. Download and install new certificate"
|
||||
echo "2. List installed certificates"
|
||||
echo "3. Check certificate expiration"
|
||||
echo "4. Exit"
|
||||
|
||||
read -r -p "Select an option (1-4): " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
if [ -z "$CERT_NAME" ]; then
|
||||
read -r -p "Enter certificate name: " CERT_NAME
|
||||
fi
|
||||
if [ -z "$API_KEY" ]; then
|
||||
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)
|
||||
check_cert_expiration() {
|
||||
echo -e "\n${BLUE}Certificate Expiration Dates:${NC}"
|
||||
for cert in "$CERT_PATH"/*.crt; do
|
||||
if [ -f "$cert" ]; then
|
||||
|
@ -138,14 +169,29 @@ main_menu() {
|
|||
openssl x509 -enddate -noout -in "$cert"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
4)
|
||||
echo -e "${GREEN}Exiting...${NC}"
|
||||
}
|
||||
|
||||
main_menu() {
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
process_certificates
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Invalid option${NC}"
|
||||
;;
|
||||
fi
|
||||
|
||||
while true; do
|
||||
echo -e "\n${BLUE}Certificate Management Menu${NC}"
|
||||
echo "1. Process all certificates"
|
||||
echo "2. List installed certificates"
|
||||
echo "3. Check certificate expiration"
|
||||
echo "4. Exit"
|
||||
|
||||
read -r -p "Select an option (1-4): " choice
|
||||
|
||||
case $choice in
|
||||
1) process_certificates ;;
|
||||
2) ls -l "$CERT_PATH"/*.crt 2>/dev/null || echo "No certificates found" ;;
|
||||
3) check_cert_expiration ;;
|
||||
4) echo -e "${GREEN}Exiting...${NC}"; exit 0 ;;
|
||||
*) echo -e "${RED}Invalid option${NC}" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
@ -153,7 +199,4 @@ main_menu() {
|
|||
# Script initialization
|
||||
check_requirements
|
||||
setup_directories
|
||||
|
||||
# Start the script
|
||||
echo -e "${GREEN}Welcome to Certwarden Certificate Management${NC}"
|
||||
main_menu
|
||||
|
|
Loading…
Add table
Reference in a new issue