update docs and install script

This commit is contained in:
Johan Siebens
2025-05-07 21:31:40 +02:00
parent d5a5a924ca
commit 8fe4342571
3 changed files with 213 additions and 78 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ Before you begin, make sure you have:
- Ports 443 (HTTPS) and 3478/UDP (STUN) open in your firewall - Ports 443 (HTTPS) and 3478/UDP (STUN) open in your firewall
- Basic familiarity with the Linux command line - Basic familiarity with the Linux command line
## Domain and DNS configuration ### Domain and DNS configuration
ionscale requires a domain name to function properly. This enables secure HTTPS connections and proper Tailscale device discovery. ionscale requires a domain name to function properly. This enables secure HTTPS connections and proper Tailscale device discovery.
@@ -47,7 +47,7 @@ First, set up environment variables for the configuration:
```bash ```bash
export IONSCALE_ACME_EMAIL="your-email@example.com" # Used for Let's Encrypt notifications export IONSCALE_ACME_EMAIL="your-email@example.com" # Used for Let's Encrypt notifications
export IONSCALE_DOMAIN="ionscale.example.com" # Your ionscale domain export IONSCALE_DOMAIN="ionscale.example.com" # Your ionscale domain
export IONSCALE_SYSTEM_ADMIN_KEY=$(docker run --rm ghcr.io/jsiebens/ionscale: genkey -n) export IONSCALE_SYSTEM_ADMIN_KEY=$(docker run --rm ghcr.io/jsiebens/ionscale:0.17.0 genkey -n)
``` ```
!!! important "System admin key" !!! important "System admin key"
+43 -4
View File
@@ -12,7 +12,7 @@ Before you begin, make sure you have:
- Ports 443 (HTTPS) and 3478/UDP (STUN) open in your firewall - Ports 443 (HTTPS) and 3478/UDP (STUN) open in your firewall
- Basic familiarity with the Linux command line - Basic familiarity with the Linux command line
## Domain and DNS configuration ### Domain and DNS configuration
ionscale requires a domain name to function properly. This enables secure HTTPS connections and proper Tailscale device discovery. ionscale requires a domain name to function properly. This enables secure HTTPS connections and proper Tailscale device discovery.
@@ -28,6 +28,42 @@ ionscale requires a domain name to function properly. This enables secure HTTPS
``` ```
The command should return your server's public IP address. The command should return your server's public IP address.
## Quick deployment
If you prefer an automated deployment, you can use our installation script:
```bash
# Download the script
curl -fsSL https://raw.githubusercontent.com/jsiebens/ionscale/main/scripts/install.sh -o install.sh
chmod +x install.sh
# Run the script (interactive mode)
./install.sh
```
The script will prompt you for:
1. Your domain name for ionscale
2. Your email address (for Let's Encrypt notifications)
For non-interactive installation, set the required environment variables:
```bash
export IONSCALE_DOMAIN="ionscale.example.com"
export IONSCALE_ACME_EMAIL="your-email@example.com"
./install.sh
```
The script automatically:
1. Determines your system architecture
2. Creates a dedicated service user
3. Downloads and installs the latest ionscale binary
4. Generates a secure system admin key
5. Creates necessary configuration files
6. Sets up and starts the systemd service
For a detailed explanation of each step, continue reading the manual installation instructions below.
## System preparation ## System preparation
### Create a dedicated service user ### Create a dedicated service user
@@ -35,13 +71,13 @@ ionscale requires a domain name to function properly. This enables secure HTTPS
For security reasons, ionscale should run under a dedicated, unprivileged system user: For security reasons, ionscale should run under a dedicated, unprivileged system user:
```bash ```bash
# Create service user
sudo useradd --system --no-create-home --shell /bin/false ionscale
# Create directories # Create directories
sudo mkdir -p /etc/ionscale sudo mkdir -p /etc/ionscale
sudo mkdir -p /var/lib/ionscale sudo mkdir -p /var/lib/ionscale
# Create service user
sudo useradd --system --no-create-home --shell /bin/false ionscale
# Set appropriate permissions # Set appropriate permissions
sudo chown ionscale:ionscale /etc/ionscale sudo chown ionscale:ionscale /etc/ionscale
sudo chown ionscale:ionscale /var/lib/ionscale sudo chown ionscale:ionscale /var/lib/ionscale
@@ -102,6 +138,9 @@ tls:
database: database:
url: "/var/lib/ionscale/ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)" url: "/var/lib/ionscale/ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)"
keys:
system_admin_key: "\${IONSCALE_KEYS_SYSTEM_ADMIN_KEY}"
logging: logging:
level: info level: info
EOF EOF
+168 -72
View File
@@ -1,166 +1,262 @@
#!/bin/bash #!/bin/bash
set -e set -e
# ionscale linux installation script
# This script automates the installation of ionscale on a Linux server with systemd
# Display functions
info() { info() {
echo '[INFO] ->' "$@" echo "===> [INFO]" "$@"
}
warn() {
echo "===> [WARN]" "$@"
} }
fatal() { fatal() {
echo '[ERROR] ->' "$@" echo "===> [ERROR]" "$@"
exit 1 exit 1
} }
verify_system() { # Welcome message
if ! [ -d /run/systemd ]; then echo "===================================================="
fatal 'Can not find systemd to use as a process supervisor for ionscale' echo "ionscale Installation Script"
fi echo "===================================================="
}
setup_env() { # Check for systemd
SUDO=sudo if ! [ -d /run/systemd ]; then
if [ "$(id -u)" -eq 0 ]; then fatal "Cannot find systemd to use as a process supervisor for ionscale"
SUDO= fi
fi
# Check for root or sudo privileges
SUDO=sudo
if [ "$(id -u)" -eq 0 ]; then
SUDO=
info "Running as root"
else
info "Running with sudo"
fi
# Get required input
if [ -z "${IONSCALE_DOMAIN}" ]; then
read -p "Enter your ionscale domain (e.g. ionscale.example.com): " IONSCALE_DOMAIN
if [ -z "${IONSCALE_DOMAIN}" ]; then if [ -z "${IONSCALE_DOMAIN}" ]; then
fatal "env variable IONSCALE_DOMAIN is undefined" fatal "Domain is required"
fi fi
fi
if [ -z "${IONSCALE_ACME_EMAIL}" ]; then
read -p "Enter your email address (for Let's Encrypt notifications): " IONSCALE_ACME_EMAIL
if [ -z "${IONSCALE_ACME_EMAIL}" ]; then if [ -z "${IONSCALE_ACME_EMAIL}" ]; then
fatal "env variable IONSCALE_ACME_EMAIL is undefined" fatal "Email address is required"
fi fi
fi
IONSCALE_VERSION=v0.12.0 # Set up directories and paths
IONSCALE_DATA_DIR=/var/lib/ionscale IONSCALE_VERSION=v0.17.0 # Updated version
IONSCALE_CONFIG_DIR=/etc/ionscale IONSCALE_DATA_DIR=/var/lib/ionscale
IONSCALE_SERVICE_FILE=/etc/systemd/system/ionscale.service IONSCALE_CONFIG_DIR=/etc/ionscale
IONSCALE_SERVICE_FILE=/etc/systemd/system/ionscale.service
IONSCALE_ENV_FILE=/etc/default/ionscale
BIN_DIR=/usr/local/bin
BIN_DIR=/usr/local/bin # --- Architecture detection ---
} setup_arch() {
info "Detecting architecture"
# --- set arch and suffix, fatal if architecture not supported ---
setup_verify_arch() {
if [ -z "$ARCH" ]; then if [ -z "$ARCH" ]; then
ARCH=$(uname -m) ARCH=$(uname -m)
fi fi
case $ARCH in case $ARCH in
amd64) amd64|x86_64)
SUFFIX=amd64 SUFFIX=amd64
;; ;;
x86_64) arm64|aarch64)
SUFFIX=amd64
;;
arm64)
SUFFIX=arm64
;;
aarch64)
SUFFIX=arm64 SUFFIX=arm64
;; ;;
*) *)
fatal "Unsupported architecture $ARCH" fatal "Unsupported architecture $ARCH"
;; ;;
esac esac
info "Architecture: $ARCH (using $SUFFIX)"
} }
has_yum() { # --- Dependencies check ---
[ -n "$(command -v yum)" ]
}
has_apt_get() {
[ -n "$(command -v apt-get)" ]
}
install_dependencies() { install_dependencies() {
info "Checking for dependencies"
if ! [ -x "$(command -v curl)" ]; then if ! [ -x "$(command -v curl)" ]; then
if $(has_apt_get); then info "Installing curl"
if [ -n "$(command -v apt-get)" ]; then
$SUDO apt-get update
$SUDO apt-get install -y curl $SUDO apt-get install -y curl
elif $(has_yum); then elif [ -n "$(command -v yum)" ]; then
$SUDO yum install -y curl $SUDO yum install -y curl
else else
fatal "Could not find apt-get or yum. Cannot install dependencies on this OS" fatal "Could not find apt-get or yum. Cannot install dependencies on this OS"
exit 1
fi fi
fi fi
} }
download_and_install() { # --- Create service user ---
info "Downloading ionscale binary" create_service_user() {
$SUDO curl -o "$BIN_DIR/ionscale" -sfL "https://github.com/jsiebens/ionscale/releases/download/${IONSCALE_VERSION}/ionscale_linux_${SUFFIX}" info "Creating service user"
$SUDO chmod +x "$BIN_DIR/ionscale"
# Only create user if it doesn't exist
if ! id ionscale &>/dev/null; then
$SUDO useradd --system --no-create-home --shell /bin/false ionscale
info "Created user 'ionscale'"
else
fatal "User 'ionscale' already exists"
fi
} }
create_folders_and_config() { # --- Binary installation ---
$SUDO mkdir --parents ${IONSCALE_DATA_DIR} download_and_install() {
$SUDO mkdir --parents ${IONSCALE_CONFIG_DIR} info "Downloading ionscale binary v$IONSCALE_VERSION"
$SUDO curl -o "$BIN_DIR/ionscale" -sfL "https://github.com/jsiebens/ionscale/releases/download/${IONSCALE_VERSION}/ionscale_linux_${SUFFIX}"
$SUDO chmod +x "$BIN_DIR/ionscale"
if [ ! -f "/etc/default/ionscale" ]; then # Verify installation
$SUDO tee /etc/default/ionscale >/dev/null <<EOF if [ -x "$BIN_DIR/ionscale" ]; then
IONSCALE_KEYS_SYSTEM_ADMIN_KEY=$($BIN_DIR/ionscale genkey -n) info "ionscale binary installed successfully"
EOF $SUDO $BIN_DIR/ionscale version
else
fatal "Failed to install ionscale binary"
fi fi
}
$SUDO tee ${IONSCALE_CONFIG_DIR}/config.yaml >/dev/null <<EOF # --- Create configuration files ---
http_listen_addr: ":80" create_folders_and_config() {
https_listen_addr: ":443" info "Creating directories"
metrics_listen_addr: 127.0.0.1:9090 $SUDO mkdir -p ${IONSCALE_DATA_DIR}
server_url: "https://${IONSCALE_DOMAIN}" $SUDO mkdir -p ${IONSCALE_CONFIG_DIR}
# Set appropriate ownership
$SUDO chown ionscale:ionscale ${IONSCALE_DATA_DIR}
$SUDO chown ionscale:ionscale ${IONSCALE_CONFIG_DIR}
info "Generating system admin key"
ADMIN_KEY=$($BIN_DIR/ionscale genkey -n)
$SUDO tee $IONSCALE_ENV_FILE >/dev/null <<EOF
IONSCALE_KEYS_SYSTEM_ADMIN_KEY=$ADMIN_KEY
EOF
info "Generated system admin key:"
echo "$ADMIN_KEY"
echo "IMPORTANT: Save this key securely. You'll need it to administer ionscale."
info "Creating configuration file"
$SUDO tee ${IONSCALE_CONFIG_DIR}/config.yaml >/dev/null <<EOF
listen_addr: ":443"
public_addr: "${IONSCALE_DOMAIN}:443"
stun_listen_addr: ":3478"
stun_public_addr: "${IONSCALE_DOMAIN}:3478"
tls: tls:
acme: true acme: true
acme_email: "${IONSCALE_ACME_EMAIL}" acme_email: "${IONSCALE_ACME_EMAIL}"
acme_path: "${IONSCALE_DATA_DIR}/acme"
database: database:
type: sqlite type: sqlite
url: "${IONSCALE_DATA_DIR}/ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)" url: "${IONSCALE_DATA_DIR}/ionscale.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(ON)"
keys:
system_admin_key: "\${IONSCALE_KEYS_SYSTEM_ADMIN_KEY}"
logging: logging:
level: info level: info
EOF EOF
info "Configuration created at ${IONSCALE_CONFIG_DIR}/config.yaml"
} }
# --- write systemd service file --- # --- Create systemd service file ---
create_systemd_service_file() { create_systemd_service_file() {
info "Adding systemd service file ${IONSCALE_SERVICE_FILE}" info "Creating systemd service file ${IONSCALE_SERVICE_FILE}"
$SUDO tee ${IONSCALE_SERVICE_FILE} >/dev/null <<EOF $SUDO tee ${IONSCALE_SERVICE_FILE} >/dev/null <<EOF
[Unit] [Unit]
Description=ionscale - a Tailscale Controller server Description=ionscale - a Tailscale control server
After=syslog.target Requires=network-online.target
After=network.target After=network.target
[Service] [Service]
EnvironmentFile=/etc/default/ionscale EnvironmentFile=/etc/default/ionscale
User=ionscale
Group=ionscale
ExecStart=${BIN_DIR}/ionscale server --config ${IONSCALE_CONFIG_DIR}/config.yaml ExecStart=${BIN_DIR}/ionscale server --config ${IONSCALE_CONFIG_DIR}/config.yaml
Restart=on-failure Restart=on-failure
RestartSec=10s RestartSec=10s
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
} }
# --- startup systemd service --- # --- Enable and start service ---
systemd_enable_and_start() { systemd_enable_and_start() {
[ "${SKIP_ENABLE}" = true ] && return if [ "${SKIP_ENABLE}" = true ]; then
info "Skipping service enablement (SKIP_ENABLE=true)"
return
fi
info "Enabling systemd service" info "Enabling systemd service"
$SUDO systemctl enable ${IONSCALE_SERVICE_FILE} >/dev/null
$SUDO systemctl daemon-reload >/dev/null $SUDO systemctl daemon-reload >/dev/null
$SUDO systemctl enable ${IONSCALE_SERVICE_FILE} >/dev/null
[ "${SKIP_START}" = true ] && return if [ "${SKIP_START}" = true ]; then
info "Skipping service start (SKIP_START=true)"
return
fi
info "Starting systemd service" info "Starting ionscale service"
$SUDO systemctl restart ionscale $SUDO systemctl restart ionscale
return 0 # Check service status
ATTEMPTS=0
MAX_ATTEMPTS=5
DELAY=2
while [ $ATTEMPTS -lt $MAX_ATTEMPTS ]; do
if $SUDO systemctl is-active --quiet ionscale; then
info "ionscale service is running"
break
else
ATTEMPTS=$((ATTEMPTS + 1))
if [ $ATTEMPTS -eq $MAX_ATTEMPTS ]; then
warn "ionscale service failed to start. Check status with: sudo systemctl status ionscale"
else
info "Waiting for service to start ($ATTEMPTS/$MAX_ATTEMPTS)..."
sleep $DELAY
fi
fi
done
} }
setup_env # Main execution sequence
setup_verify_arch setup_arch
verify_system
install_dependencies install_dependencies
create_service_user
download_and_install download_and_install
create_folders_and_config create_folders_and_config
create_systemd_service_file create_systemd_service_file
systemd_enable_and_start systemd_enable_and_start
# Completion message
echo
echo "===================================================="
echo "ionscale installation complete!"
echo "===================================================="
echo
echo "Your ionscale instance is now available at: https://${IONSCALE_DOMAIN}"
echo
echo "Next steps:"
echo "1. Configure OIDC authentication if needed"
echo "2. Set up DNS provider if needed"
echo "3. Create your first tailnet"
echo
echo "To view logs: sudo journalctl -u ionscale -f"
echo "To restart the service: sudo systemctl restart ionscale"
echo
echo "Configure ionscale CLI:"
echo "export IONSCALE_ADDR=https://${IONSCALE_DOMAIN}"
echo "export IONSCALE_SYSTEM_ADMIN_KEY=${ADMIN_KEY}"
echo "Then you can run: ionscale tailnets list"