Archived
247 lines
8.3 KiB
Bash
247 lines
8.3 KiB
Bash
#!/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:-}"
|
||
|
|
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}"
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
cat <<'EOF'
|
||
|
|
KarlDNS Linux installer
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
install-linux.sh [options]
|
||
|
|
|
||
|
|
Options:
|
||
|
|
--base-url URL KarlDNS server URL. Default: https://karldns.de
|
||
|
|
--raw-base-url URL Script download base URL. Default: GitHub raw main branch
|
||
|
|
--every MINUTES Cron interval, 1-60. Default: 5
|
||
|
|
--mode MODE auto, system, or user. Default: auto
|
||
|
|
--update-url URL Existing private /u/<secret> update URL
|
||
|
|
--hostname NAME Optional display hostname for output/config
|
||
|
|
--dashboard-url URL Optional private dashboard URL for output/config
|
||
|
|
--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 ;;
|
||
|
|
--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)"
|
||
|
|
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"
|
||
|
|
}
|
||
|
|
|
||
|
|
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"
|
||
|
|
}
|
||
|
|
|
||
|
|
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")"
|
||
|
|
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"
|
||
|
|
cron_file="/etc/cron.d/karldns-updater"
|
||
|
|
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
|
||
|
|
cat > "$cron_file" <<EOF
|
||
|
|
SHELL=/bin/sh
|
||
|
|
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
|
|
$(cron_schedule) root KARLDNS_CONFIG=$(quote_sh "$config_file") $(quote_sh "$updater_path") >/dev/null 2>&1
|
||
|
|
EOF
|
||
|
|
chmod 644 "$cron_file"
|
||
|
|
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
|
||
|
|
echo "System install needs root. Re-run with sudo or set KARLDNS_INSTALL_MODE=user." >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "$KARLDNS_UPDATE_URL" ]; then
|
||
|
|
echo "KarlDNS: creating a new hostname at $KARLDNS_BASE_URL ..."
|
||
|
|
response="$("$curl_bin" -fsS --max-time 30 -H 'content-type: application/json' -d '{}' "$KARLDNS_BASE_URL/api/v1/hostnames/magic")"
|
||
|
|
KARLDNS_UPDATE_URL="$(printf '%s' "$response" | json_field update_url)"
|
||
|
|
KARLDNS_HOSTNAME="$(printf '%s' "$response" | json_field hostname)"
|
||
|
|
KARLDNS_MANAGEMENT_URL="$(printf '%s' "$response" | json_field management_url)"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "$KARLDNS_UPDATE_URL" ]; then
|
||
|
|
echo "KarlDNS installer: could not get an update URL" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
case "$KARLDNS_UPDATE_URL" in
|
||
|
|
*"<ipaddr>"*|*"<ip6addr>"*) KARLDNS_UPDATE_URL="${KARLDNS_UPDATE_URL%%\?*}" ;;
|
||
|
|
esac
|
||
|
|
|
||
|
|
if [ "$KARLDNS_INSTALL_MODE" = "system" ]; then
|
||
|
|
install_system
|
||
|
|
else
|
||
|
|
install_user
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "KarlDNS installed."
|
||
|
|
echo "Hostname: ${KARLDNS_HOSTNAME:-already configured}"
|
||
|
|
echo "Dashboard: ${KARLDNS_MANAGEMENT_URL:-use your existing private status page}"
|
||
|
|
echo "Mode: $KARLDNS_INSTALL_MODE"
|
||
|
|
if [ "$KARLDNS_SKIP_CRON" = "0" ]; then
|
||
|
|
echo "Interval: every $KARLDNS_INTERVAL_MINUTES minute(s)"
|
||
|
|
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
|