Compare commits

...

2 commits

Author SHA1 Message Date
47db5038ac Cleaned up before merge 2025-05-07 13:04:49 +02:00
bde2444b17 Fetch PEM from certwarden API 2025-05-07 10:56:26 +02:00
3 changed files with 57 additions and 82 deletions

View file

@ -6,15 +6,10 @@ A bash script for managing SSL/TLS certificates through the Certwarden API. This
- Download and verify certificates and private keys from Certwarden server - Download and verify certificates and private keys from Certwarden server
- Automatic installation with proper permissions and ownership - Automatic installation with proper permissions and ownership
- Certificate and key pair validation
- Service reload after certificate updates
- Certificate expiration monitoring - Certificate expiration monitoring
- Interactive menu-driven interface - Interactive menu-driven interface
- Silent mode for automated operations
- Force update option for certificate renewals - Force update option for certificate renewals
- Proper error handling and logging
- Support for multiple certificates - Support for multiple certificates
- Secure temporary file handling
## Prerequisites ## Prerequisites
@ -25,26 +20,26 @@ The script requires the following dependencies:
## Installation ## Installation
1. Clone this repository: 1. Clone this repository into `/opt/certman/`:
```bash ```bash
git clone <repository-url> git clone https://forge.dmz.skyfritt.net/Skyfritt/certman.git --depth=1 /opt/certman && cd certman
cd certman
``` ```
2. Create a `.env` file with your configuration: 2. Rename `example.env` to `.env` and modify as needed:
```bash ```bash
# Server Configuration # Server Configuration
CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443" CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443"
# Certificate Paths # Certificate Paths
CERT_PATH="/etc/forgejo" CERT_PATH="/etc/certs"
KEY_PATH="/etc/forgejo" KEY_PATH="/etc/certs"
TEMP_PATH="/tmp/certman" TEMP_PATH="/tmp/certman"
FULLCHAIN_PEM="true"
# Service Configuration # Service Configuration
SERVICE_NAME="forgejo" SERVICE_NAME="nginx"
CERT_OWNER="git" CERT_OWNER="www-data"
CERT_GROUP="git" CERT_GROUP="www-data"
CERT_PERMISSIONS="644" CERT_PERMISSIONS="644"
KEY_PERMISSIONS="600" KEY_PERMISSIONS="600"
@ -55,11 +50,6 @@ CERTIFICATES='[
"domain": "example-one.com", "domain": "example-one.com",
"cert_api_key": "your_cert_api_key", "cert_api_key": "your_cert_api_key",
"key_api_key": "your_key_api_key" "key_api_key": "your_key_api_key"
},
{
"domain": "example-two.com",
"cert_api_key": "your_cert_api_key",
"key_api_key": "your_key_api_key"
} }
]' ]'
``` ```
@ -67,20 +57,20 @@ CERTIFICATES='[
## Usage ## Usage
### Interactive Mode ### Interactive Mode
Run the script without any arguments: Run the script without any arguments
```bash ```bash
./certman.sh ./certman.sh
``` ```
This will present a menu with the following options: ### Cron Configuration
1. Process all certificates Add these lines to your crontab for automated certificate management:
2. List installed certificates ```cron
3. Check certificate expiration @reboot sleep 15 && /path/to/certman.sh --silent
4. Force update all certificates 2 2 * * 7 /opt/certman/certman.sh --silent
5. Exit ```
### Automated Mode ### Silent Mode
Run the script with the `--silent` flag for automated operations: Run the script with the `--silent` flag for a silent certificate check. It will use the options set in the `.env` file.
```bash ```bash
./certman.sh --silent ./certman.sh --silent
``` ```
@ -91,21 +81,10 @@ Use the `--force` flag to force certificate updates regardless of current status
./certman.sh --force ./certman.sh --force
``` ```
Flags can be combined: ### Disable fetching fullchain PEM
Use the `--disable-pem` to only fetch the .key and .crt:
```bash ```bash
./certman.sh --silent --force ./certman.sh --disable-pem
```
### Fullchain PEM
Use the `--fullchain-pem` flag to combine certificate and private key into a single PEM file:
```bash
./certman.sh --fullchain-pem
### Cron Configuration
Add these lines to your crontab for automated certificate management:
```cron
@reboot sleep 15 && /path/to/certman.sh --silent
5 4 * * 2 /path/to/certman.sh --silent
``` ```
## Environment Variables ## Environment Variables
@ -116,19 +95,10 @@ Add these lines to your crontab for automated certificate management:
| CERT_PATH | Directory for certificate storage | Yes | | CERT_PATH | Directory for certificate storage | Yes |
| KEY_PATH | Directory for private key storage | Yes | | KEY_PATH | Directory for private key storage | Yes |
| TEMP_PATH | Temporary directory for downloads | Yes | | TEMP_PATH | Temporary directory for downloads | Yes |
| FULLCHAIN_PEM | Optional: Combine cert and key into single PEM file | No | | FULLCHAIN_PEM | Enabled by default | No |
| SERVICE_NAME | Service to reload after certificate updates | Yes | | SERVICE_NAME | Service to reload after certificate updates | Yes |
| CERT_OWNER | User owner for certificate files | Yes | | CERT_OWNER | User owner for certificate files | Yes |
| CERT_GROUP | Group owner for certificate files | Yes | | CERT_GROUP | Group owner for certificate files | Yes |
| CERT_PERMISSIONS | Certificate file permissions | Yes | | CERT_PERMISSIONS | Certificate file permissions | Yes |
| KEY_PERMISSIONS | Private key file permissions | Yes | | KEY_PERMISSIONS | Private key file permissions | Yes |
| CERTIFICATES | JSON array of certificate configurations | Yes | | CERTIFICATES | JSON array of certificate configurations | Yes |
## 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
- Temporary files are automatically cleaned up using secure practices
- Certificate and key pairs are validated before installation

View file

@ -26,7 +26,7 @@ load_env || exit 1
AUTO_MODE="false" AUTO_MODE="false"
FORCE_UPDATE="false" FORCE_UPDATE="false"
FULLCHAIN_PEM="${FULLCHAIN_PEM:-false}" FULLCHAIN_PEM="${FULLCHAIN_PEM:-true}"
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case $1 in case $1 in
@ -38,8 +38,8 @@ while [[ $# -gt 0 ]]; do
FORCE_UPDATE="true" FORCE_UPDATE="true"
shift shift
;; ;;
--fullchain-pem) --disable-pem)
FULLCHAIN_PEM="true" FULLCHAIN_PEM="false"
shift shift
;; ;;
*) *)
@ -87,8 +87,8 @@ download_and_verify_cert() {
local key_api_key=$3 local key_api_key=$3
local temp_cert="$TEMP_DIR/$domain.crt" local temp_cert="$TEMP_DIR/$domain.crt"
local temp_key="$TEMP_DIR/$domain.key" local temp_key="$TEMP_DIR/$domain.key"
local temp_cert_pem="$TEMP_DIR/$domain.cert.pem" local temp_pem="$TEMP_DIR/$domain.pem"
local temp_key_pem="$TEMP_DIR/$domain.key.pem"
echo -e "${BLUE}Processing certificate for $domain${NC}" echo -e "${BLUE}Processing certificate for $domain${NC}"
@ -106,18 +106,19 @@ download_and_verify_cert() {
return 1 return 1
fi fi
# Download fullchain PEM file
if ! curl -s -fL -o "$temp_pem" -H "X-API-Key: $cert_api_key.$key_api_key" \
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatecertchains/$domain"; then
echo -e "${RED}Failed to download fullchain PEM file for $domain${NC}"
return 1
fi
# Verify files are not empty # Verify files are not empty
if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ]; then if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ] || [ ! -s "$temp_pem" ]; then
echo -e "${RED}Downloaded files are empty for $domain${NC}" echo -e "${RED}Downloaded files are empty for $domain${NC}"
return 1 return 1
fi fi
# Create PEM files if requested
if [ "$FULLCHAIN_PEM" = "true" ]; then
cat "$temp_cert" > "$temp_cert_pem"
cat "$temp_key" > "$temp_key_pem"
fi
# Validate certificate and key match # Validate certificate and key match
local cert_fingerprint local cert_fingerprint
cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey | cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey |
@ -126,12 +127,21 @@ download_and_verify_cert() {
local key_fingerprint local key_fingerprint
key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null | key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null |
openssl dgst -sha256) openssl dgst -sha256)
local pem_fingerprint
pem_fingerprint=$(openssl x509 -in "$temp_pem" -noout -pubkey |
openssl pkey -pubin -outform DER 2>/dev/null |
openssl dgst -sha256)
if [ "$cert_fingerprint" != "$key_fingerprint" ]; then if [ "$cert_fingerprint" != "$key_fingerprint" ]; then
echo -e "${RED}Certificate and key do not match for $domain${NC}" echo -e "${RED}Certificate and key do not match for $domain${NC}"
return 1 return 1
fi fi
if [[ "$cert_fingerprint" != "$pem_fingerprint" ]]; then
echo -e "${RED}Certificate and PEM file do not match for $domain${NC}"
return 1
fi
return 0 return 0
} }
@ -139,19 +149,17 @@ install_certificate() {
local domain=$1 local domain=$1
local final_cert="$CERT_PATH/$domain.crt" local final_cert="$CERT_PATH/$domain.crt"
local final_key="$KEY_PATH/$domain.key" local final_key="$KEY_PATH/$domain.key"
local final_cert_pem="$CERT_PATH/$domain.cert.pem" local final_pem="$KEY_PATH/$domain.pem"
local final_key_pem="$KEY_PATH/$domain.key.pem"
local temp_cert="$TEMP_DIR/$domain.crt" local temp_cert="$TEMP_DIR/$domain.crt"
local temp_key="$TEMP_DIR/$domain.key" local temp_key="$TEMP_DIR/$domain.key"
local temp_cert_pem="$TEMP_DIR/$domain.cert.pem" local temp_pem="$TEMP_DIR/$domain.pem"
local temp_key_pem="$TEMP_DIR/$domain.key.pem"
local needs_reload=0 local needs_reload=0
# Check if certificate needs updating # Check if certificate needs updating
if [ "$FORCE_UPDATE" = "true" ]; then if [ "$FORCE_UPDATE" = "true" ]; then
needs_reload=1 needs_reload=1
elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_cert_pem" ]; then elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_pem" ]; then
if ! cmp -s "$final_cert_pem" "$temp_cert_pem"; then if ! cmp -s "$final_pem" "$temp_cert_pem"; then
needs_reload=1 needs_reload=1
fi fi
elif [ -f "$final_cert" ]; then elif [ -f "$final_cert" ]; then
@ -165,11 +173,11 @@ install_certificate() {
# Install new certificate and key # Install new certificate and key
if [ $needs_reload -eq 1 ]; then if [ $needs_reload -eq 1 ]; then
if [ "$FULLCHAIN_PEM" = "true" ]; then if [ "$FULLCHAIN_PEM" = "true" ]; then
if ! cp -f "$temp_cert_pem" "$final_cert_pem" || ! cp -f "$temp_key_pem" "$final_key_pem"; then if ! cp -f "$temp_pem" "$final_pem"; then
echo -e "${RED}Failed to install PEM files for $domain${NC}" echo -e "${RED}Failed to install PEM file for $domain${NC}"
return 1 return 1
fi fi
local files=("$final_cert_pem" "$final_key_pem") local files=("$final_pem")
else else
if ! cp -f "$temp_cert" "$final_cert" || ! cp -f "$temp_key" "$final_key"; 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}" echo -e "${RED}Failed to install certificate files for $domain${NC}"

View file

@ -1,19 +1,19 @@
CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443" CERTWARDEN_SERVER="certwarden.dmz.skyfritt.net:443"
# Certificate Paths # Certificate Paths
CERT_PATH="/etc/forgejo" CERT_PATH="/etc/certs"
KEY_PATH="/etc/forgejo" KEY_PATH="/etc/certs"
TEMP_PATH="/tmp/certman" TEMP_PATH="/tmp/certman"
# FULLCHAIN_PEM=true
# Service Configuration # Service Configuration
SERVICE_NAME="forgejo" SERVICE_NAME="nginx"
CERT_OWNER="git" CERT_OWNER="www-data"
CERT_GROUP="git" CERT_GROUP="www-data"
CERT_PERMISSIONS="644" CERT_PERMISSIONS="644"
KEY_PERMISSIONS="600" KEY_PERMISSIONS="600"
# Certificate Configurations (JSON format) # Certificate Configurations (JSON format)
# Add as many or few domains as you need (but remember to add or delete the JSON comma!)
CERTIFICATES='[ CERTIFICATES='[
{ {
"domain": "example.com", "domain": "example.com",
@ -21,6 +21,3 @@ CERTIFICATES='[
"key_api_key": "your_key_api_key" "key_api_key": "your_key_api_key"
} }
]' ]'
# Optional: Auto mode configuration
AUTO_MODE="false"