This repository has been archived on 2026-10-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
karldns.de/scripts/install-linux.sh
T

309 lines
10 KiB
Bash
Raw Normal View History

2026-05-20 23:55:27 +02:00
#!/bin/sh
set -eu
KARLDNS_BASE_URL="${KARLDNS_BASE_URL:-https://karldns.de}"
KARLDNS_INTERVAL_MINUTES="${KARLDNS_INTERVAL_MINUTES:-5}"
KARLDNS_INSTALL_MODE="${KARLDNS_INSTALL_MODE:-auto}"
KARLDNS_UPDATE_URL="${KARLDNS_UPDATE_URL:-}"
KARLDNS_HOSTNAME="${KARLDNS_HOSTNAME:-}"
KARLDNS_MANAGEMENT_URL="${KARLDNS_MANAGEMENT_URL:-}"
2026-05-21 17:23:08 +02:00
KARLDNS_ACTIVATION_URL="${KARLDNS_ACTIVATION_URL:-}"
2026-05-20 23:55:27 +02:00
KARLDNS_RAW_BASE_URL="${KARLDNS_RAW_BASE_URL:-https://raw.githubusercontent.com/StasonJatham/karldns.de/main/scripts}"
KARLDNS_IPV4="${KARLDNS_IPV4:-auto}"
KARLDNS_IPV6="${KARLDNS_IPV6:-auto}"
KARLDNS_SKIP_CRON="${KARLDNS_SKIP_CRON:-0}"
KARLDNS_SKIP_FIRST_UPDATE="${KARLDNS_SKIP_FIRST_UPDATE:-0}"
2026-05-21 17:23:08 +02:00
KARLDNS_REUSED_CONFIG=0
2026-05-20 23:55:27 +02:00
usage() {
cat <<'EOF'
KarlDNS Linux installer
Usage:
install-linux.sh [options]
Options:
--base-url URL KarlDNS server URL. Default: https://karldns.de
2026-05-21 10:05:40 +02:00
--raw-base-url URL Updater script download URL
--every MINUTES Update interval, 1-60. Default: 5
2026-05-20 23:55:27 +02:00
--mode MODE auto, system, or user. Default: auto
2026-05-21 10:05:40 +02:00
--update-url URL Existing private update URL
--hostname NAME Hostname to show after install
--dashboard-url URL Private dashboard URL to show after install
2026-05-21 17:23:08 +02:00
--activation-url URL Private registration URL for pending hostnames
2026-05-20 23:55:27 +02:00
--ipv4 VALUE auto, off, or a static IPv4. Default: auto
--ipv6 VALUE auto, off, or a static IPv6. Default: auto
--no-cron Install files only, do not configure cron
--no-run Do not run the first update after install
-h, --help Show this help
Examples:
curl -fsSL https://raw.githubusercontent.com/StasonJatham/karldns.de/main/scripts/install-linux.sh | sudo sh
curl -fsSL https://raw.githubusercontent.com/StasonJatham/karldns.de/main/scripts/install-linux.sh | sh -s -- --mode user
EOF
}
while [ "$#" -gt 0 ]; do
case "$1" in
--base-url) KARLDNS_BASE_URL="${2:?missing value for --base-url}"; shift 2 ;;
--raw-base-url) KARLDNS_RAW_BASE_URL="${2:?missing value for --raw-base-url}"; shift 2 ;;
--every) KARLDNS_INTERVAL_MINUTES="${2:?missing value for --every}"; shift 2 ;;
--mode) KARLDNS_INSTALL_MODE="${2:?missing value for --mode}"; shift 2 ;;
--update-url) KARLDNS_UPDATE_URL="${2:?missing value for --update-url}"; shift 2 ;;
--hostname) KARLDNS_HOSTNAME="${2:?missing value for --hostname}"; shift 2 ;;
--dashboard-url|--management-url) KARLDNS_MANAGEMENT_URL="${2:?missing value for --dashboard-url}"; shift 2 ;;
2026-05-21 17:23:08 +02:00
--activation-url) KARLDNS_ACTIVATION_URL="${2:?missing value for --activation-url}"; shift 2 ;;
2026-05-20 23:55:27 +02:00
--ipv4) KARLDNS_IPV4="${2:?missing value for --ipv4}"; shift 2 ;;
--ipv6) KARLDNS_IPV6="${2:?missing value for --ipv6}"; shift 2 ;;
--no-cron) KARLDNS_SKIP_CRON=1; shift ;;
--no-run) KARLDNS_SKIP_FIRST_UPDATE=1; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "KarlDNS installer: unknown option: $1" >&2; usage >&2; exit 1 ;;
esac
done
need() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "KarlDNS installer: missing required command: $1" >&2
exit 1
fi
}
json_field() {
key="$1"
if command -v python3 >/dev/null 2>&1; then
python3 -c 'import json,sys; print(json.load(sys.stdin).get(sys.argv[1], ""))' "$key"
else
sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" | head -n 1
fi
}
quote_sh() {
printf "%s" "$1" | sed "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"
}
download_updater() {
target_path="$1"
tmp_path="$(mktemp)"
2026-05-21 17:23:08 +02:00
mkdir -p "$(dirname "$target_path")"
2026-05-20 23:55:27 +02:00
if ! "$curl_bin" -fsS "$KARLDNS_RAW_BASE_URL/karldns-update.sh" -o "$tmp_path"; then
rm -f "$tmp_path"
exit 1
fi
chmod 755 "$tmp_path"
mv "$tmp_path" "$target_path"
}
2026-05-21 17:23:08 +02:00
load_existing_config() {
if [ -n "$KARLDNS_UPDATE_URL" ]; then
return
fi
config_file="$1"
if [ -r "$config_file" ]; then
# shellcheck disable=SC1090
. "$config_file"
KARLDNS_REUSED_CONFIG=1
fi
}
2026-05-20 23:55:27 +02:00
cron_schedule() {
if [ "$KARLDNS_INTERVAL_MINUTES" = "60" ]; then
printf '0 * * * *'
else
printf '*/%s * * * *' "$KARLDNS_INTERVAL_MINUTES"
fi
}
update_crontab() {
cron_file="$1"
schedule="$2"
command="$3"
marker_start="# karldns-update start"
marker_end="# karldns-update end"
awk -v start="$marker_start" -v end="$marker_end" '
$0 == start {skip = 1; next}
$0 == end {skip = 0; next}
skip != 1 {print}
' "$cron_file" 2>/dev/null
printf '%s\n' "$marker_start"
printf '%s %s\n' "$schedule" "$command"
printf '%s\n' "$marker_end"
}
2026-05-21 17:23:08 +02:00
install_system_cron() {
schedule="$1"
command="$2"
if [ -d /etc/cron.d ]; then
cron_file="/etc/cron.d/karldns-updater"
cat > "$cron_file" <<EOF
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$schedule root $command
EOF
chmod 644 "$cron_file"
return
fi
if [ -d /etc/crontabs ]; then
cron_file="/etc/crontabs/root"
tmp_file="$(mktemp)"
update_crontab "$cron_file" "$schedule" "$command" > "$tmp_file"
cat "$tmp_file" > "$cron_file"
rm -f "$tmp_file"
chmod 600 "$cron_file" 2>/dev/null || true
return
fi
if command -v crontab >/dev/null 2>&1; then
cron_file="$(mktemp)"
crontab -l > "$cron_file" 2>/dev/null || true
update_crontab "$cron_file" "$schedule" "$command" > "$cron_file.new"
mv "$cron_file.new" "$cron_file"
crontab "$cron_file"
rm -f "$cron_file"
return
fi
echo "KarlDNS installer: cron is not available. Install cron or re-run with --no-cron." >&2
exit 1
}
2026-05-20 23:55:27 +02:00
write_config() {
config_file="$1"
cache_file="$2"
lock_file="$3"
umask 077
{
printf 'KARLDNS_BASE_URL=%s\n' "$(quote_sh "$KARLDNS_BASE_URL")"
printf 'KARLDNS_UPDATE_URL=%s\n' "$(quote_sh "$KARLDNS_UPDATE_URL")"
printf 'KARLDNS_HOSTNAME=%s\n' "$(quote_sh "$KARLDNS_HOSTNAME")"
printf 'KARLDNS_MANAGEMENT_URL=%s\n' "$(quote_sh "$KARLDNS_MANAGEMENT_URL")"
2026-05-21 17:23:08 +02:00
printf 'KARLDNS_ACTIVATION_URL=%s\n' "$(quote_sh "$KARLDNS_ACTIVATION_URL")"
2026-05-20 23:55:27 +02:00
printf 'KARLDNS_CACHE_FILE=%s\n' "$(quote_sh "$cache_file")"
printf 'KARLDNS_LOCK_FILE=%s\n' "$(quote_sh "$lock_file")"
printf 'KARLDNS_IPV4=%s\n' "$(quote_sh "$KARLDNS_IPV4")"
printf 'KARLDNS_IPV6=%s\n' "$(quote_sh "$KARLDNS_IPV6")"
} > "$config_file"
chmod 600 "$config_file"
}
install_user() {
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/karldns"
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/karldns"
bin_dir="${HOME}/.local/bin"
config_file="${config_dir}/updater.env"
updater_path="${bin_dir}/karldns-update"
cron_file="$(mktemp)"
mkdir -p "$config_dir" "$cache_dir" "$bin_dir"
download_updater "$updater_path"
chmod 700 "$updater_path"
write_config "$config_file" "$cache_dir/last-ip" "$cache_dir/updater.lock"
if [ "$KARLDNS_SKIP_CRON" = "0" ]; then
need crontab
crontab -l > "$cron_file" 2>/dev/null || true
update_crontab "$cron_file" "$(cron_schedule)" \
"KARLDNS_CONFIG=$(quote_sh "$config_file") $(quote_sh "$updater_path") >/dev/null 2>&1" > "$cron_file.new"
mv "$cron_file.new" "$cron_file"
crontab "$cron_file"
rm -f "$cron_file"
fi
}
install_system() {
config_dir="/etc/karldns"
cache_dir="/var/cache/karldns"
config_file="${config_dir}/updater.env"
updater_path="/usr/local/bin/karldns-update"
mkdir -p "$config_dir" "$cache_dir"
download_updater "$updater_path"
write_config "$config_file" "$cache_dir/last-ip" "/run/karldns-updater.lock"
if [ "$KARLDNS_SKIP_CRON" = "0" ]; then
2026-05-21 17:23:08 +02:00
install_system_cron "$(cron_schedule)" "KARLDNS_CONFIG=$(quote_sh "$config_file") $(quote_sh "$updater_path") >/dev/null 2>&1"
2026-05-20 23:55:27 +02:00
fi
}
need curl
curl_bin="$(command -v curl)"
case "$KARLDNS_INTERVAL_MINUTES" in
''|*[!0-9]*) echo "KARLDNS_INTERVAL_MINUTES must be a positive integer" >&2; exit 1 ;;
esac
if [ "$KARLDNS_INTERVAL_MINUTES" -lt 1 ] || [ "$KARLDNS_INTERVAL_MINUTES" -gt 60 ]; then
echo "KARLDNS_INTERVAL_MINUTES must be between 1 and 60" >&2
exit 1
fi
case "$KARLDNS_INSTALL_MODE" in
auto)
if [ "$(id -u)" -eq 0 ]; then
KARLDNS_INSTALL_MODE="system"
else
KARLDNS_INSTALL_MODE="user"
fi
;;
user|system) ;;
*) echo "KARLDNS_INSTALL_MODE must be auto, user, or system" >&2; exit 1 ;;
esac
if [ "$KARLDNS_INSTALL_MODE" = "system" ] && [ "$(id -u)" -ne 0 ]; then
2026-05-21 10:05:40 +02:00
echo "System install needs root. Re-run with sudo or use --mode user." >&2
2026-05-20 23:55:27 +02:00
exit 1
fi
2026-05-21 17:23:08 +02:00
if [ "$KARLDNS_INSTALL_MODE" = "system" ]; then
load_existing_config /etc/karldns/updater.env
else
load_existing_config "${XDG_CONFIG_HOME:-$HOME/.config}/karldns/updater.env"
fi
2026-05-20 23:55:27 +02:00
if [ -z "$KARLDNS_UPDATE_URL" ]; then
echo "KarlDNS: creating a new hostname at $KARLDNS_BASE_URL ..."
2026-05-21 17:23:08 +02:00
response="$("$curl_bin" -fsS --max-time 30 -X POST "$KARLDNS_BASE_URL/api/v1/hostnames")"
2026-05-20 23:55:27 +02:00
KARLDNS_UPDATE_URL="$(printf '%s' "$response" | json_field update_url)"
KARLDNS_HOSTNAME="$(printf '%s' "$response" | json_field hostname)"
2026-05-21 17:23:08 +02:00
KARLDNS_MANAGEMENT_URL="$(printf '%s' "$response" | json_field dashboard_url)"
KARLDNS_ACTIVATION_URL="$(printf '%s' "$response" | json_field registration_url)"
2026-05-20 23:55:27 +02:00
fi
if [ -z "$KARLDNS_UPDATE_URL" ]; then
2026-05-21 10:05:40 +02:00
echo "KarlDNS installer: could not create an update URL" >&2
2026-05-20 23:55:27 +02:00
exit 1
fi
case "$KARLDNS_UPDATE_URL" in
*"<ipaddr>"*|*"<ip6addr>"*) KARLDNS_UPDATE_URL="${KARLDNS_UPDATE_URL%%\?*}" ;;
esac
2026-05-21 17:23:08 +02:00
if [ -n "$KARLDNS_ACTIVATION_URL" ] && [ "$KARLDNS_REUSED_CONFIG" = "0" ]; then
echo "KarlDNS: registering hostname ..."
"$curl_bin" -fsS --max-time 30 -X POST "$KARLDNS_ACTIVATION_URL" >/dev/null
fi
2026-05-20 23:55:27 +02:00
if [ "$KARLDNS_INSTALL_MODE" = "system" ]; then
install_system
else
install_user
fi
echo "KarlDNS installed."
echo "Hostname: ${KARLDNS_HOSTNAME:-already configured}"
2026-05-21 10:05:40 +02:00
echo "Dashboard: ${KARLDNS_MANAGEMENT_URL:-use your existing private dashboard}"
2026-05-20 23:55:27 +02:00
echo "Mode: $KARLDNS_INSTALL_MODE"
if [ "$KARLDNS_SKIP_CRON" = "0" ]; then
2026-05-21 10:05:40 +02:00
echo "Interval: every $KARLDNS_INTERVAL_MINUTES minutes"
2026-05-20 23:55:27 +02:00
else
echo "Cron: skipped"
fi
if [ "$KARLDNS_SKIP_FIRST_UPDATE" = "0" ]; then
echo "Running first update now..."
if [ "$KARLDNS_INSTALL_MODE" = "system" ]; then
KARLDNS_CONFIG=/etc/karldns/updater.env /usr/local/bin/karldns-update
else
KARLDNS_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/karldns/updater.env" "$HOME/.local/bin/karldns-update"
fi
fi