mirror of
https://github.com/jsiebens/ionscale.git
synced 2026-03-31 15:07:49 +01:00
update docs and install script
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
Reference in New Issue
Block a user