Compare commits
2 commits
1895042b55
...
47db5038ac
Author | SHA1 | Date | |
---|---|---|---|
47db5038ac | |||
bde2444b17 |
3 changed files with 57 additions and 82 deletions
74
README.md
74
README.md
|
@ -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
|
|
||||||
|
|
50
certman.sh
50
certman.sh
|
@ -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,16 +106,17 @@ download_and_verify_cert() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify files are not empty
|
# Download fullchain PEM file
|
||||||
if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ]; then
|
if ! curl -s -fL -o "$temp_pem" -H "X-API-Key: $cert_api_key.$key_api_key" \
|
||||||
echo -e "${RED}Downloaded files are empty for $domain${NC}"
|
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatecertchains/$domain"; then
|
||||||
|
echo -e "${RED}Failed to download fullchain PEM file for $domain${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create PEM files if requested
|
# Verify files are not empty
|
||||||
if [ "$FULLCHAIN_PEM" = "true" ]; then
|
if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ] || [ ! -s "$temp_pem" ]; then
|
||||||
cat "$temp_cert" > "$temp_cert_pem"
|
echo -e "${RED}Downloaded files are empty for $domain${NC}"
|
||||||
cat "$temp_key" > "$temp_key_pem"
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate certificate and key match
|
# Validate certificate and key match
|
||||||
|
@ -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}"
|
||||||
|
|
15
example.env
15
example.env
|
@ -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"
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue