Compare commits
13 Commits
13444c5ceb
...
wizard
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11efff6829 | ||
|
|
83ada5bd70 | ||
|
|
6109e54a63 | ||
|
|
735e9a758b | ||
|
|
b5998954ab | ||
|
|
d9dfe3aa7b | ||
|
|
2387bb1e6a | ||
|
|
c2253fb62a | ||
|
|
84776ad57b | ||
|
|
57ceb8d4ec | ||
|
|
705241f451 | ||
|
|
43f898a2db | ||
|
|
0584fb4865 |
@@ -2,7 +2,7 @@
|
|||||||
debug
|
debug
|
||||||
}
|
}
|
||||||
|
|
||||||
:8443 {
|
:443 {
|
||||||
tls /certs/soteria.crt /certs/soteria.key {
|
tls /certs/soteria.crt /certs/soteria.key {
|
||||||
protocols tls1.3
|
protocols tls1.3
|
||||||
client_auth {
|
client_auth {
|
||||||
|
|||||||
94
README.md
94
README.md
@@ -14,41 +14,13 @@ Connect solely through wireguard to `192.168.1.142` and serve the REST server wi
|
|||||||
|
|
||||||
[REST backend](https://restic.readthedocs.io/en/latest/100_references.html#rest-backend)
|
[REST backend](https://restic.readthedocs.io/en/latest/100_references.html#rest-backend)
|
||||||
|
|
||||||
## Certificates
|
|
||||||
|
|
||||||
[Certificate Renewal](https://smallstep.com/docs/step-ca/renewal/)
|
|
||||||
|
|
||||||
Generate a new private key and (public) certificate
|
|
||||||
|
|
||||||
```
|
|
||||||
step ca certificate soteria.john-stream.com certs/soteria.crt certs/soteria.key --provisioner admin
|
|
||||||
```
|
|
||||||
|
|
||||||
One-time setup for Caddy to be able to trust the Janus CA. This creates a symlink for the root CA.
|
|
||||||
|
|
||||||
Check certificate
|
|
||||||
```
|
|
||||||
openssl x509 -noout -subject -issuer -ext extendedKeyUsage -in certs/soteria.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
cat certs/soteria.crt certs/soteria.key > $(step path)/certs/soteria.pem
|
|
||||||
```
|
|
||||||
|
|
||||||
Add to ~/.bashrc to trust the Janus CA:
|
|
||||||
|
|
||||||
```
|
|
||||||
export RESTIC_CACERT=$(step path)/certs/root_ca.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a test repo through the rest server:
|
|
||||||
|
|
||||||
```
|
|
||||||
restic -r rest:https://soteria.john-stream.com:8443/dev-test --tls-client-cert certs/client_combined.pem init
|
|
||||||
```
|
|
||||||
|
|
||||||
## Restic Repos
|
## Restic Repos
|
||||||
|
|
||||||
|
`/etc/fstab` entry:
|
||||||
|
```
|
||||||
|
john-nas:/volume1/restic /mnt/nfs/restic nfs nofail,_netdev,x-systemd.automount,x-systemd.idle-timeout=600,timeo=14,retrans=3,hard,tcp,nfsvers=3 0 0
|
||||||
|
```
|
||||||
|
|
||||||
Mounted using a bind mount point in the LXC.
|
Mounted using a bind mount point in the LXC.
|
||||||
|
|
||||||
https://pve.proxmox.com/wiki/Linux_Container#_bind_mount_points
|
https://pve.proxmox.com/wiki/Linux_Container#_bind_mount_points
|
||||||
@@ -57,14 +29,58 @@ https://pve.proxmox.com/wiki/Linux_Container#_bind_mount_points
|
|||||||
pct set 103 -mp0 /mnt/nfs/restic,mp=/mnt/restic
|
pct set 103 -mp0 /mnt/nfs/restic,mp=/mnt/restic
|
||||||
```
|
```
|
||||||
|
|
||||||
## Restic Clients
|
## Soteria Certificates
|
||||||
|
|
||||||
|
[Certificate Renewal](https://smallstep.com/docs/step-ca/renewal/)
|
||||||
|
|
||||||
|
Generate a new private key and (public) certificate in the right places. This will use the `admin` provisioner.
|
||||||
|
|
||||||
|
```
|
||||||
|
step ca certificate soteria.john-stream.com certs/soteria.crt certs/soteria.key --provisioner admin
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the resultant certificate:
|
||||||
|
|
||||||
|
```
|
||||||
|
openssl x509 -noout -subject -issuer -ext extendedKeyUsage -ext subjectAltName -in certs/soteria.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
Set up renewal
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./scripts/install_services.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Test renewal
|
||||||
|
|
||||||
|
```
|
||||||
|
systemctl start cert-renewer.service && \
|
||||||
|
systemctl status cert-renewer.service --no-pager && \
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clients
|
||||||
|
|
||||||
|
To set up a client, run the following command. It will prompt for the provisioner password and the repository name.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sL https://gitea.john-stream.com/john/soteria/raw/branch/main/scripts/setup_client.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sL https://gitea.john-stream.com/john/soteria/raw/branch/main/scripts/check_status.sh | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Setup
|
||||||
|
|
||||||
Set up provisioner password by running this and pasting in the current JWK provisioner password for `admin`
|
Set up provisioner password by running this and pasting in the current JWK provisioner password for `admin`
|
||||||
|
|
||||||
```
|
```
|
||||||
read -s secret && (umask 077; echo "$secret" > secret.txt)
|
read -s secret && (umask 077; echo "$secret" > $(step path)/certs/secret.txt)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Generate the client TLS private key and (public) certificate for mTLS. This will combine them both into a file called `restic.pem`, which can be used with the `--tls-client-cert` option with the restic CLI.
|
||||||
|
|
||||||
```
|
```
|
||||||
cd $(step path)/certs && \
|
cd $(step path)/certs && \
|
||||||
step ca certificate \
|
step ca certificate \
|
||||||
@@ -78,6 +94,14 @@ Need restic 0.16+ for the env vars `RESTIC_CACERT` and `RESTIC_TLS_CLIENT_CERT`
|
|||||||
```
|
```
|
||||||
export RESTIC_CACERT=$(step path)/certs/root_ca.crt
|
export RESTIC_CACERT=$(step path)/certs/root_ca.crt
|
||||||
export RESTIC_TLS_CLIENT_CERT=$(step path)/certs/restic.pem
|
export RESTIC_TLS_CLIENT_CERT=$(step path)/certs/restic.pem
|
||||||
|
export RESTIC_REPOSITORY=rest:https://soteria.john-stream.com/john-ubuntu
|
||||||
|
export RESTIC_PASSWORD_FILE=$(readlink -f ~/.config/resticprofile/password.txt)
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a test repo through the rest server:
|
||||||
|
|
||||||
|
```
|
||||||
|
restic snapshots
|
||||||
```
|
```
|
||||||
|
|
||||||
### Installing Latest Binary
|
### Installing Latest Binary
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
rest-server:
|
rest-server:
|
||||||
image: restic/rest-server
|
image: restic/rest-server
|
||||||
|
container_name: restic
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /mnt/restic:/data
|
- /mnt/restic:/data
|
||||||
@@ -9,13 +10,14 @@ services:
|
|||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:alpine
|
image: caddy:alpine
|
||||||
|
container_name: caddy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8443:8443"
|
- "443:443"
|
||||||
volumes:
|
volumes:
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
- ./certs/soteria.crt:/certs/soteria.crt:ro
|
- ./certs/soteria.crt:/certs/soteria.crt:ro
|
||||||
- ./certs/soteria.key:/certs/soteria.key:ro
|
- ./certs/soteria.key:/certs/soteria.key:ro
|
||||||
- ${HOME}/.step/certs/root_ca.crt:/certs/root_ca.crt:ro
|
- ${HOME}/.step/certs/root_ca.crt:/certs/root_ca.crt:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- rest-server
|
- rest-server
|
||||||
65
scripts/check_status.sh
Executable file
65
scripts/check_status.sh
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
print_status() {
|
||||||
|
local item="$1"
|
||||||
|
local status="$2"
|
||||||
|
local error_msg="$3"
|
||||||
|
if [ "$status" -eq 0 ]; then
|
||||||
|
echo -e "${item}: ${GREEN}OK${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${item}: ${RED}FAIL${NC}"
|
||||||
|
if [ -n "$error_msg" ]; then
|
||||||
|
echo -e " ${RED}Error:${NC} $error_msg"
|
||||||
|
fi
|
||||||
|
EXIT_CODE=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
EXIT_CODE=0
|
||||||
|
|
||||||
|
CERTS_DIR="$(readlink -f ~/.step/certs)"
|
||||||
|
SERVER_CERT="$CERTS_DIR/restic.crt"
|
||||||
|
SERVER_KEY="$CERTS_DIR/restic.key"
|
||||||
|
TIMER_NAME="cert-renewer.timer"
|
||||||
|
|
||||||
|
# 1. Check Certificates Existence
|
||||||
|
if [ -f "$SERVER_CERT" ] && [ -f "$SERVER_KEY" ]; then
|
||||||
|
print_status "Certificate Files" 0
|
||||||
|
else
|
||||||
|
print_status "Certificate Files" 1 "Missing $SERVER_CERT or $SERVER_KEY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Check Certificate Validity (Is it valid NOW?)
|
||||||
|
if [ -f "$SERVER_CERT" ] && command -v openssl &> /dev/null; then
|
||||||
|
# Check if valid for at least 60 seconds
|
||||||
|
if openssl x509 -checkend 60 -noout -in "$SERVER_CERT" &> /dev/null; then
|
||||||
|
print_status "Certificate Validity" 0
|
||||||
|
else
|
||||||
|
print_status "Certificate Validity" 1 "Certificate at $SERVER_CERT is expired or expiring within 60s"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Check Timer Status
|
||||||
|
if systemctl is-active "$TIMER_NAME" &> /dev/null; then
|
||||||
|
print_status "Renewal Timer" 0
|
||||||
|
else
|
||||||
|
# Check if unit exists
|
||||||
|
if systemctl list-unit-files "$TIMER_NAME" &> /dev/null; then
|
||||||
|
print_status "Renewal Timer" 1 "Systemd timer '$TIMER_NAME' is installed but not active"
|
||||||
|
else
|
||||||
|
# Check if source file exists
|
||||||
|
TIMER_FILE="/etc/systemd/system/$TIMER_NAME"
|
||||||
|
if [ -f "$TIMER_FILE" ]; then
|
||||||
|
print_status "Renewal Timer" 1 "Systemd timer '$TIMER_NAME' is not installed (found source at $TIMER_FILE)"
|
||||||
|
else
|
||||||
|
print_status "Renewal Timer" 1 "Systemd timer '$TIMER_NAME' is missing entirely (expected at $TIMER_FILE)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit $EXIT_CODE
|
||||||
139
scripts/setup_client.sh
Executable file
139
scripts/setup_client.sh
Executable file
@@ -0,0 +1,139 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() { echo -e "${YELLOW}[INFO]${NC} $1"; }
|
||||||
|
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
check_command() {
|
||||||
|
if ! command -v "$1" &> /dev/null; then
|
||||||
|
log_error "$1 is required but not installed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_command step
|
||||||
|
check_command curl
|
||||||
|
check_command wget
|
||||||
|
check_command bunzip2
|
||||||
|
|
||||||
|
# 1. Setup Step Certificates
|
||||||
|
log_info "Setting up Step Certificates..."
|
||||||
|
|
||||||
|
STEP_PATH="$(step path)"
|
||||||
|
CERTS_DIR="$STEP_PATH/certs"
|
||||||
|
|
||||||
|
if [ ! -d "$CERTS_DIR" ]; then
|
||||||
|
log_info "Creating directory $CERTS_DIR"
|
||||||
|
mkdir -p "$CERTS_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt for secret securely (reading from /dev/tty to support pipe execution)
|
||||||
|
echo -e "${YELLOW}Please enter the provisioner password for 'admin':${NC}"
|
||||||
|
read -s secret < /dev/tty
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -z "$secret" ]; then
|
||||||
|
log_error "Password cannot be empty."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt for Repo Name
|
||||||
|
DEFAULT_REPO_NAME=$(hostnamectl hostname 2>/dev/null || hostname)
|
||||||
|
echo -e "${YELLOW}Please enter the Restic Repository Name (default: $DEFAULT_REPO_NAME):${NC}"
|
||||||
|
read repo_name < /dev/tty
|
||||||
|
|
||||||
|
if [ -z "$repo_name" ]; then
|
||||||
|
repo_name="$DEFAULT_REPO_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save secret temporarily
|
||||||
|
SECRET_FILE="$CERTS_DIR/secret.txt"
|
||||||
|
(umask 077; echo "$secret" > "$SECRET_FILE")
|
||||||
|
log_success "Secret saved to $SECRET_FILE"
|
||||||
|
|
||||||
|
# Generate Certificates
|
||||||
|
log_info "Generating certificates for repo/client: $repo_name"
|
||||||
|
|
||||||
|
cd "$CERTS_DIR"
|
||||||
|
|
||||||
|
if step ca certificate \
|
||||||
|
--provisioner admin --password-file secret.txt \
|
||||||
|
"$repo_name" restic.crt restic.key; then
|
||||||
|
|
||||||
|
# Combine into PEM
|
||||||
|
(umask 077; cat restic.crt restic.key > restic.pem)
|
||||||
|
log_success "Certificates generated and combined into restic.pem"
|
||||||
|
|
||||||
|
# Clean up secret? The README keeps it, but usually it's good to ask.
|
||||||
|
# The README implies keeping it for renewal maybe?
|
||||||
|
# But for client certs, renewal might need the password again if using the same provisioner.
|
||||||
|
# I'll leave it as per README instructions.
|
||||||
|
else
|
||||||
|
log_error "Failed to generate certificates. Check your password and connection to the CA."
|
||||||
|
rm -f "$SECRET_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Install Restic
|
||||||
|
log_info "Checking for Restic..."
|
||||||
|
|
||||||
|
if ! command -v restic &> /dev/null; then
|
||||||
|
log_info "Restic not found. Installing latest version..."
|
||||||
|
|
||||||
|
RESTIC_VERSION="0.18.1"
|
||||||
|
DOWNLOAD_URL="https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2"
|
||||||
|
|
||||||
|
TMP_DIR=$(mktemp -d)
|
||||||
|
pushd "$TMP_DIR" > /dev/null
|
||||||
|
|
||||||
|
wget -q -O restic.bz2 "$DOWNLOAD_URL"
|
||||||
|
bunzip2 restic.bz2
|
||||||
|
chmod +x restic
|
||||||
|
|
||||||
|
log_info "Installing restic to /usr/local/bin (requires sudo)..."
|
||||||
|
if sudo mv restic /usr/local/bin/; then
|
||||||
|
log_success "Restic installed successfully."
|
||||||
|
else
|
||||||
|
log_error "Failed to move restic to /usr/local/bin"
|
||||||
|
popd > /dev/null
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
popd > /dev/null
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
else
|
||||||
|
CURRENT_VERSION=$(restic version | awk '{print $2}')
|
||||||
|
log_success "Restic is already installed (version $CURRENT_VERSION)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Final Instructions
|
||||||
|
ROOT_CA="$CERTS_DIR/root_ca.crt"
|
||||||
|
CLIENT_PEM="$CERTS_DIR/restic.pem"
|
||||||
|
|
||||||
|
# Ensure root_ca exists (it should if step is bootstrapped)
|
||||||
|
if [ ! -f "$ROOT_CA" ]; then
|
||||||
|
log_info "Downloading Root CA..."
|
||||||
|
step ca root "$ROOT_CA"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_success "Setup complete!"
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}=== Environment Configuration ===${NC}"
|
||||||
|
echo "Add the following lines to your shell configuration (.bashrc, .zshrc, etc) or script:"
|
||||||
|
echo ""
|
||||||
|
echo "export RESTIC_CACERT=$ROOT_CA"
|
||||||
|
echo "export RESTIC_TLS_CLIENT_CERT=$CLIENT_PEM"
|
||||||
|
echo "export RESTIC_REPOSITORY=rest:https://soteria.john-stream.com/$repo_name"
|
||||||
|
echo "export RESTIC_PASSWORD_FILE=~/.config/resticprofile/password.txt"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Note: Adjust RESTIC_REPOSITORY and RESTIC_PASSWORD_FILE as needed.${NC}"
|
||||||
145
scripts/setup_wizard.sh
Executable file
145
scripts/setup_wizard.sh
Executable file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() {
|
||||||
|
echo -e "${YELLOW}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Input Framework
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Ensure we have a tty for input
|
||||||
|
if [ ! -e /dev/tty ]; then
|
||||||
|
echo "Error: Script must be run in an interactive terminal (cannot find /dev/tty)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to prompt for user input
|
||||||
|
# Usage: get_input "VARIABLE_NAME" "Prompt Text" "Default Value" "is_secret(true/false)"
|
||||||
|
get_input() {
|
||||||
|
local var_name="$1"
|
||||||
|
local prompt_text="$2"
|
||||||
|
local default_value="$3"
|
||||||
|
local is_secret="$4"
|
||||||
|
|
||||||
|
local input_val=""
|
||||||
|
local prompt_full="${GREEN}${prompt_text}${NC}"
|
||||||
|
|
||||||
|
if [ -n "$default_value" ]; then
|
||||||
|
prompt_full+=" [$default_value]"
|
||||||
|
fi
|
||||||
|
prompt_full+=": "
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
# Print prompt to stderr so it shows up even if stdout is redirected
|
||||||
|
if [ "$is_secret" == "true" ]; then
|
||||||
|
echo -ne "$prompt_full" >&2
|
||||||
|
read -s input_val < /dev/tty
|
||||||
|
echo "" >&2 # Newline after secret input
|
||||||
|
else
|
||||||
|
echo -ne "$prompt_full" >&2
|
||||||
|
read input_val < /dev/tty
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use default if input is empty
|
||||||
|
if [ -z "$input_val" ] && [ -n "$default_value" ]; then
|
||||||
|
input_val="$default_value"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validation: Require input if no default exists
|
||||||
|
if [ -z "$input_val" ]; then
|
||||||
|
echo -e "${RED}Error: This value is required.${NC}" >&2
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Set the variable dynamically in the parent scope
|
||||||
|
printf -v "$var_name" "%s" "$input_val"
|
||||||
|
export "$var_name=$input_val"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to confirm collected inputs
|
||||||
|
# Usage: confirm_inputs "VAR1" "VAR2" "VAR3" ...
|
||||||
|
confirm_inputs() {
|
||||||
|
echo "" >&2
|
||||||
|
echo -e "${GREEN}=== Configuration Summary ===${NC}" >&2
|
||||||
|
|
||||||
|
for var in "$@"; do
|
||||||
|
local val="${!var}"
|
||||||
|
# Mask secrets in summary if needed, or just show length
|
||||||
|
# For now, just printing value.
|
||||||
|
# To improve: pass a list of secret vars to mask them.
|
||||||
|
echo -e "${YELLOW}$var:${NC} $val" >&2
|
||||||
|
done
|
||||||
|
echo "" >&2
|
||||||
|
|
||||||
|
get_input "CONFIRM" "Is this correct? (y/n)" "y" "false"
|
||||||
|
if [[ "${CONFIRM,,}" != "y" ]]; then
|
||||||
|
echo -e "${RED}Aborted by user.${NC}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_unit() {
|
||||||
|
local template_url=$1
|
||||||
|
local filename=$(basename "$template_url")
|
||||||
|
local dest_path=/etc/systemd/system/"$filename"
|
||||||
|
|
||||||
|
log_info "Installing $filename..."
|
||||||
|
|
||||||
|
curl -sL $template_url | envsubst > "$dest_path"
|
||||||
|
log_success "$filename installed to $dest_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Script Logic
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
echo "Starting Interactive Setup..."
|
||||||
|
echo "-----------------------------"
|
||||||
|
|
||||||
|
# 1. Collect Inputs
|
||||||
|
# Example:
|
||||||
|
get_input "HOST_NAME" "Enter Hostname" "$(hostname)" "false"
|
||||||
|
get_input "CERT_DIR" "Enter directory for certificates" "$(step path)/certs" "false"
|
||||||
|
get_input "CERT_LOCATION" "Enter specific path for cert" "${CERT_DIR}/${HOSTNAME}.crt" "false"
|
||||||
|
get_input "KEY_LOCATION" "Enter specific path for private key" "${CERT_DIR}/${HOSTNAME}.key" "false"
|
||||||
|
|
||||||
|
export CERT_LOCATION=$(readlink -f $CERT_LOCATION)
|
||||||
|
export KEY_LOCATION=$(readlink -f $KEY_LOCATION)
|
||||||
|
|
||||||
|
# 2. Confirm
|
||||||
|
confirm_inputs "CERT_LOCATION" "KEY_LOCATION"
|
||||||
|
|
||||||
|
# 3. Configure
|
||||||
|
REPO_URL_BASE=https://gitea.john-stream.com/john/soteria/raw/branch/main/
|
||||||
|
SERVICE_TEMPLATE_URL="${REPO_URL_BASE}systemd/cert-renewer.service"
|
||||||
|
TIMER_TEMPLATE_URL="${REPO_URL_BASE}systemd/cert-renewer.timer"
|
||||||
|
|
||||||
|
# 3. Execute
|
||||||
|
# echo "Configuring $HOST_NAME..."
|
||||||
|
install_unit ${SERVICE_TEMPLATE_URL}
|
||||||
|
install_unit ${TIMER_TEMPLATE_URL}
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now "$(basename "${TIMER_TEMPLATE_URL}")"
|
||||||
|
|
||||||
|
systemctl status "$(basename "${SERVICE_TEMPLATE_URL}")" --no-pager
|
||||||
|
systemctl status "$(basename "${TIMER_TEMPLATE_URL}")" --no-pager
|
||||||
24
systemd/cert-renewer.service
Normal file
24
systemd/cert-renewer.service
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Certificate renewal
|
||||||
|
After=network-online.target
|
||||||
|
Documentation=https://smallstep.com/docs/step-ca/certificate-authority-server-production
|
||||||
|
StartLimitIntervalSec=0
|
||||||
|
; PartOf=cert-renewer.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=root
|
||||||
|
|
||||||
|
; ExecCondition checks if the certificate is ready for renewal,
|
||||||
|
; based on the exit status of the command.
|
||||||
|
; (In systemd <242, you can use ExecStartPre= here.)
|
||||||
|
ExecCondition=/usr/bin/step certificate needs-renewal ${CERT_LOCATION}
|
||||||
|
|
||||||
|
; ExecStart renews the certificate, if ExecStartPre was successful.
|
||||||
|
ExecStart=/usr/bin/step ca renew --force ${CERT_LOCATION} ${KEY_LOCATION}
|
||||||
|
|
||||||
|
; ExecStartPost=/usr/bin/openssl x509 -noout -enddate -in ${CERT_LOCATION}
|
||||||
|
; ExecStartPost=/usr/bin/docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
19
systemd/cert-renewer.timer
Normal file
19
systemd/cert-renewer.timer
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Timer for certificate renewal
|
||||||
|
Documentation=https://smallstep.com/docs/step-ca/certificate-authority-server-production
|
||||||
|
; PartOf=cert-renewer.target
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
; Run the timer unit every 15 minutes.
|
||||||
|
OnCalendar=*:1/15
|
||||||
|
|
||||||
|
; Always run the timer on time.
|
||||||
|
AccuracySec=1us
|
||||||
|
|
||||||
|
; Add jitter to prevent a "thundering hurd" of simultaneous certificate renewals.
|
||||||
|
RandomizedDelaySec=5m
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
Reference in New Issue
Block a user