Compare commits
6 Commits
2387bb1e6a
...
wizard
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11efff6829 | ||
|
|
83ada5bd70 | ||
|
|
6109e54a63 | ||
|
|
735e9a758b | ||
|
|
b5998954ab | ||
|
|
d9dfe3aa7b |
31
README.md
31
README.md
@@ -16,6 +16,11 @@ Connect solely through wireguard to `192.168.1.142` and serve the REST server wi
|
|||||||
|
|
||||||
## 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
|
||||||
@@ -40,8 +45,34 @@ Check the resultant certificate:
|
|||||||
openssl x509 -noout -subject -issuer -ext extendedKeyUsage -ext subjectAltName -in certs/soteria.crt
|
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
|
## 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`
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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,6 +10,7 @@ services:
|
|||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:alpine
|
image: caddy:alpine
|
||||||
|
container_name: caddy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "443:443"
|
- "443:443"
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check for sudo/root
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
|
||||||
log_error "Please run as root or with sudo"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine paths
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
|
||||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
||||||
SYSTEMD_DIR="$PROJECT_ROOT/systemd"
|
|
||||||
DEST_DIR="/etc/systemd/system"
|
|
||||||
|
|
||||||
SERVICE_FILE="cert-renewer.service"
|
|
||||||
TIMER_FILE="cert-renewer.timer"
|
|
||||||
|
|
||||||
install_unit() {
|
|
||||||
local unit_file=$1
|
|
||||||
local src_path="$SYSTEMD_DIR/$unit_file"
|
|
||||||
local dest_path="$DEST_DIR/$unit_file"
|
|
||||||
|
|
||||||
if [ ! -f "$src_path" ]; then
|
|
||||||
log_error "Source file not found: $src_path"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Installing $unit_file..."
|
|
||||||
|
|
||||||
# Remove existing link or file if it exists to ensure clean install
|
|
||||||
if [ -L "$dest_path" ] || [ -f "$dest_path" ]; then
|
|
||||||
log_info "Removing existing $dest_path"
|
|
||||||
rm -f "$dest_path"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create symlink
|
|
||||||
ln -s "$src_path" "$dest_path"
|
|
||||||
|
|
||||||
if [ -L "$dest_path" ]; then
|
|
||||||
log_success "Linked $src_path to $dest_path"
|
|
||||||
else
|
|
||||||
log_error "Failed to link $unit_file"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main execution
|
|
||||||
log_info "Starting installation of systemd services..."
|
|
||||||
|
|
||||||
install_unit "$SERVICE_FILE"
|
|
||||||
install_unit "$TIMER_FILE"
|
|
||||||
|
|
||||||
log_info "Reloading systemd daemon..."
|
|
||||||
systemctl daemon-reload
|
|
||||||
log_success "Systemd daemon reloaded"
|
|
||||||
|
|
||||||
log_info "Enabling and starting $TIMER_FILE..."
|
|
||||||
systemctl enable --now "$TIMER_FILE"
|
|
||||||
log_success "$TIMER_FILE enabled and started"
|
|
||||||
|
|
||||||
log_info "Checking status of $TIMER_FILE..."
|
|
||||||
if systemctl is-active --quiet "$TIMER_FILE"; then
|
|
||||||
systemctl status "$TIMER_FILE" --no-pager
|
|
||||||
echo ""
|
|
||||||
log_success "Installation complete!"
|
|
||||||
else
|
|
||||||
log_error "$TIMER_FILE is not active"
|
|
||||||
systemctl status "$TIMER_FILE" --no-pager
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
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
|
||||||
@@ -9,9 +9,6 @@ StartLimitIntervalSec=0
|
|||||||
Type=oneshot
|
Type=oneshot
|
||||||
User=root
|
User=root
|
||||||
|
|
||||||
Environment=CERT_LOCATION=/home/john/soteria/certs/soteria.crt \
|
|
||||||
KEY_LOCATION=/home/john/soteria/certs/soteria.key
|
|
||||||
|
|
||||||
; ExecCondition checks if the certificate is ready for renewal,
|
; ExecCondition checks if the certificate is ready for renewal,
|
||||||
; based on the exit status of the command.
|
; based on the exit status of the command.
|
||||||
; (In systemd <242, you can use ExecStartPre= here.)
|
; (In systemd <242, you can use ExecStartPre= here.)
|
||||||
@@ -20,8 +17,8 @@ ExecCondition=/usr/bin/step certificate needs-renewal ${CERT_LOCATION}
|
|||||||
; ExecStart renews the certificate, if ExecStartPre was successful.
|
; ExecStart renews the certificate, if ExecStartPre was successful.
|
||||||
ExecStart=/usr/bin/step ca renew --force ${CERT_LOCATION} ${KEY_LOCATION}
|
ExecStart=/usr/bin/step ca renew --force ${CERT_LOCATION} ${KEY_LOCATION}
|
||||||
|
|
||||||
ExecStartPost=/usr/bin/openssl x509 -noout -enddate -in ${CERT_LOCATION}
|
; ExecStartPost=/usr/bin/openssl x509 -noout -enddate -in ${CERT_LOCATION}
|
||||||
ExecStartPost=/usr/bin/docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
; ExecStartPost=/usr/bin/docker exec caddy caddy reload --config /etc/caddy/Caddyfile
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user