From eb596d23648fec13de38215defe261817f350883 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:05:17 +0100 Subject: [PATCH] core: add IPv6 fallback support to get_current_ip functions | add check for SSH_KEYS_FILE in user_defaults (#11067) * fix(core): add IPv6 fallback support to get_current_ip functions Fixes IP detection in IPv6-only environments by adding fallback: - Try IPv4 first (existing behavior) - Fall back to IPv6 via interface lookup (eth0 scope global) - Fall back to IPv6 routing with Google/Cloudflare DNS targets Closes #issue-ipv6-only-environment * fix(build): use SSH_AUTHORIZED_KEY from user defaults when no SSH_KEYS_FILE exists When using 'User Defaults' with var_ssh_authorized_key set in default.vars, the SSH key was not being installed because install_ssh_keys_into_ct() only checked for SSH_KEYS_FILE (which is only created in Advanced Settings). Now the function also checks SSH_AUTHORIZED_KEY and creates a temporary SSH_KEYS_FILE if needed. Fixes #11062 --- misc/build.func | 26 +++++++++++++++++++++++--- misc/core.func | 36 +++++++++++++++++++++++++++++++----- misc/tools.func | 22 ++++++++++++++++++++-- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0f796de72..d0eb8cd4f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -160,17 +160,29 @@ maxkeys_check() { # # - Returns current container IP depending on OS type # - Debian/Ubuntu: uses `hostname -I` -# - Alpine: parses eth0 via `ip -4 addr` +# - Alpine: parses eth0 via `ip -4 addr` or `ip -6 addr` +# - Supports IPv6-only environments as fallback # - Returns "Unknown" if OS type cannot be determined # ------------------------------------------------------------------------------ get_current_ip() { + CURRENT_IP="" if [ -f /etc/os-release ]; then # Check for Debian/Ubuntu (uses hostname -I) if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') + # Try IPv4 first + CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) + # Fallback to IPv6 if no IPv4 + if [[ -z "$CURRENT_IP" ]]; then + CURRENT_IP=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) + fi # Check for Alpine (uses ip command) elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + # Try IPv4 first + CURRENT_IP=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + # Fallback to IPv6 if no IPv4 + if [[ -z "$CURRENT_IP" ]]; then + CURRENT_IP=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n 1) + fi else CURRENT_IP="Unknown" fi @@ -202,6 +214,7 @@ update_motd_ip() { # # - Installs SSH keys into container root account if SSH is enabled # - Uses pct push or direct input to authorized_keys +# - Supports both SSH_KEYS_FILE (from advanced settings) and SSH_AUTHORIZED_KEY (from user defaults) # - Falls back to warning if no keys provided # ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { @@ -210,6 +223,13 @@ install_ssh_keys_into_ct() { # Ensure SSH_KEYS_FILE is defined (may not be set if advanced_settings was skipped) : "${SSH_KEYS_FILE:=}" + # If SSH_KEYS_FILE doesn't exist but SSH_AUTHORIZED_KEY is set (from user defaults), + # create a temporary SSH_KEYS_FILE with the key + if [[ -z "$SSH_KEYS_FILE" || ! -s "$SSH_KEYS_FILE" ]] && [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then + SSH_KEYS_FILE="$(mktemp)" + printf '%s\n' "$SSH_AUTHORIZED_KEY" >"$SSH_KEYS_FILE" + fi + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then msg_info "Installing selected SSH keys into CT ${CTID}" pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { diff --git a/misc/core.func b/misc/core.func index c306d9363..f50052080 100644 --- a/misc/core.func +++ b/misc/core.func @@ -924,14 +924,14 @@ function get_lxc_ip() { get_current_ip() { local ip - # Try direct interface lookup for eth0 FIRST (most reliable for LXC) + # Try direct interface lookup for eth0 FIRST (most reliable for LXC) - IPv4 ip=$(ip -4 addr show eth0 2>/dev/null | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "$ip" return 0 fi - # Fallback: Try hostname -I + # Fallback: Try hostname -I (returns IPv4 first if available) if command -v hostname >/dev/null 2>&1; then ip=$(hostname -I 2>/dev/null | awk '{print $1}') if [[ -n "$ip" && "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then @@ -940,9 +940,9 @@ function get_lxc_ip() { fi fi - # Last resort: Use routing table - local targets=("8.8.8.8" "1.1.1.1" "default") - for target in "${targets[@]}"; do + # Try routing table with IPv4 targets + local ipv4_targets=("8.8.8.8" "1.1.1.1" "default") + for target in "${ipv4_targets[@]}"; do if [[ "$target" == "default" ]]; then ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') else @@ -954,6 +954,32 @@ function get_lxc_ip() { fi done + # IPv6 fallback: Try direct interface lookup for eth0 + ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + + # IPv6 fallback: Try hostname -I for IPv6 + if command -v hostname >/dev/null 2>&1; then + ip=$(hostname -I 2>/dev/null | tr ' ' '\n' | grep -E ':' | head -n1) + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + fi + + # IPv6 fallback: Use routing table with IPv6 targets + local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111") + for target in "${ipv6_targets[@]}"; do + ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + done + return 1 } diff --git a/misc/tools.func b/misc/tools.func index 12aed29bd..1392adfe0 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3548,10 +3548,11 @@ IP_FILE="/run/local-ip.env" mkdir -p "$(dirname "$IP_FILE")" get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") local ip - for target in "${targets[@]}"; do + # Try IPv4 targets first + local ipv4_targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + for target in "${ipv4_targets[@]}"; do if [[ "$target" == "default" ]]; then ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') else @@ -3563,6 +3564,23 @@ get_current_ip() { fi done + # IPv6 fallback: Try direct interface lookup for eth0 + ip=$(ip -6 addr show eth0 scope global 2>/dev/null | awk '/inet6 / {print $2}' | cut -d/ -f1 | head -n1) + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + + # IPv6 fallback: Use routing table with IPv6 targets (Google DNS, Cloudflare DNS) + local ipv6_targets=("2001:4860:4860::8888" "2606:4700:4700::1111") + for target in "${ipv6_targets[@]}"; do + ip=$(ip -6 route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + if [[ -n "$ip" && "$ip" =~ : ]]; then + echo "$ip" + return 0 + fi + done + return 1 }