#!/bin/bash # shellcheck disable=SC1091 source "${AGENT_PATH}/common.sh" _check_required_env_vars "CONTAINER_NAME" "LXC_IMAGE" "TAILSCALE_AUTH_KEY" "HOST_INTERFACE" "STATIC_IP" "GATEWAY" "RELAY_PORT" # Check LXC is available if ! command -v lxc &>/dev/null; then _die "LXC is not installed. Install it with: sudo snap install lxd --channel=latest/stable && sudo lxd init" fi # Check LXC daemon is accessible if ! lxc list &>/dev/null; then _die "Cannot connect to LXD. Ensure the LXD daemon is running and your user is in the 'lxd' group." fi echo "Installing Tailscale Relay (LXC)..." # Stop and remove existing container if it exists if lxc info "${CONTAINER_NAME}" &>/dev/null; then echo "Removing existing container..." lxc stop "${CONTAINER_NAME}" --force 2>/dev/null || true lxc delete "${CONTAINER_NAME}" --force 2>/dev/null || true fi # Create the container (without starting) echo "Creating LXC container from ${LXC_IMAGE}..." lxc init "${LXC_IMAGE}" "${CONTAINER_NAME}" || _die "Failed to create LXC container" # Configure bridged networking on the host interface echo "Configuring bridged networking on ${HOST_INTERFACE}..." lxc config device remove "${CONTAINER_NAME}" eth0 2>/dev/null || true lxc config device add "${CONTAINER_NAME}" eth0 nic \ nictype=macvlan \ parent="${HOST_INTERFACE}" || _die "Failed to configure network device" # Enable nesting (required when running LXC inside another LXC, e.g. Proxmox) lxc config set "${CONTAINER_NAME}" security.nesting=true # Allow TUN device inside the container (needed by tailscale's WireGuard interface) lxc config set "${CONTAINER_NAME}" raw.lxc "lxc.cgroup2.devices.allow = c 10:200 rwm" lxc config device add "${CONTAINER_NAME}" tun unix-char path=/dev/net/tun 2>/dev/null || true # Start the container echo "Starting container..." lxc start "${CONTAINER_NAME}" || _die "Failed to start container" # Wait for container to be ready echo "Waiting for container to be ready..." READY=false for i in $(seq 1 30); do if lxc exec "${CONTAINER_NAME}" -- test -d /etc/netplan 2>/dev/null; then READY=true break fi sleep 1 done if [ "$READY" != "true" ]; then _die "Container did not become ready in time" fi # Extract IP without CIDR for display CONTAINER_IP="${STATIC_IP%%/*}" # Configure static IP via netplan echo "Configuring static IP ${STATIC_IP}..." lxc exec "${CONTAINER_NAME}" -- bash -c "rm -f /etc/netplan/*.yaml" lxc exec "${CONTAINER_NAME}" -- bash -c "cat > /etc/netplan/10-static.yaml << 'NETPLANEOF' network: version: 2 ethernets: eth0: dhcp4: false addresses: - ${STATIC_IP} routes: - to: default via: ${GATEWAY} nameservers: addresses: [${DNS_SERVERS}] NETPLANEOF" lxc exec "${CONTAINER_NAME}" -- chmod 600 /etc/netplan/10-static.yaml lxc exec "${CONTAINER_NAME}" -- netplan apply 2>/dev/null || true # Wait for network to come up sleep 3 # Verify network connectivity echo "Verifying network connectivity..." if ! lxc exec "${CONTAINER_NAME}" -- ping -c 1 -W 5 "${GATEWAY}" &>/dev/null; then echo "Warning: Cannot reach gateway ${GATEWAY} from container. Check HOST_INTERFACE and network settings." fi # Install Tailscale inside the container echo "Installing Tailscale inside the container..." lxc exec "${CONTAINER_NAME}" -- bash -c "curl -fsSL https://tailscale.com/install.sh | sh" || _die "Failed to install Tailscale" # Connect to Tailscale network echo "Connecting to Tailscale network..." TAILSCALE_UP_CMD="tailscale up --authkey=${TAILSCALE_AUTH_KEY}" if [ -n "$TAILSCALE_HOSTNAME" ]; then TAILSCALE_UP_CMD="${TAILSCALE_UP_CMD} --hostname=${TAILSCALE_HOSTNAME}" fi if [ -n "$TAILSCALE_EXTRA_ARGS" ]; then TAILSCALE_UP_CMD="${TAILSCALE_UP_CMD} ${TAILSCALE_EXTRA_ARGS}" fi # Execute tailscale up with retries RETRY_COUNT=0 MAX_RETRIES=5 RETRY_DELAY=10 while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if lxc exec "${CONTAINER_NAME}" -- ${TAILSCALE_UP_CMD} 2>&1; then echo "Successfully connected to Tailscale network!" break else RETRY_COUNT=$((RETRY_COUNT + 1)) if [ $RETRY_COUNT -lt $MAX_RETRIES ]; then echo "Connection attempt $RETRY_COUNT failed. Retrying in ${RETRY_DELAY} seconds..." sleep $RETRY_DELAY else echo "Warning: Failed to connect after $MAX_RETRIES attempts." echo "You may need to connect manually using:" echo " lxc exec ${CONTAINER_NAME} -- tailscale up" fi fi done # Enable peer relay on the configured port echo "Enabling peer relay on port ${RELAY_PORT}/udp..." lxc exec "${CONTAINER_NAME}" -- tailscale set --relay-server-port="${RELAY_PORT}" || echo "Warning: Failed to enable peer relay. You may need to enable it manually." # Get the Tailscale IP TAILSCALE_IP=$(lxc exec "${CONTAINER_NAME}" -- tailscale ip -4 2>/dev/null || echo "not yet available") echo "" echo "==========================================" echo "Tailscale Relay installation complete!" echo "==========================================" echo "" echo " LAN IP: ${CONTAINER_IP}" echo " Tailscale IP: ${TAILSCALE_IP}" echo " Relay port: ${RELAY_PORT}/udp" echo " Container: ${CONTAINER_NAME}" echo "" echo "The container has its own IP (${CONTAINER_IP}) on your LAN." echo "Forward ${RELAY_PORT}/udp on your router to ${CONTAINER_IP}." echo "" echo "You also need to add relay ACL grants in your tailnet policy." echo "See: https://tailscale.com/kb/1403/peer-relay" echo "" echo "Manage at: https://login.tailscale.com/admin/machines"