|
36 | 36 | cores = 1; |
37 | 37 | }; |
38 | 38 |
|
39 | | - # Networking: DHCPv4 + static IPv6 (Hetzner doesn't provide DHCPv6 or RA) |
| 39 | + # Networking: DHCPv4 + static IPv6 from metadata API |
| 40 | + # Hetzner Cloud doesn't send Router Advertisements — the VM must configure |
| 41 | + # IPv6 statically. The address/gateway are fetched from the metadata API at |
| 42 | + # boot and written as a networkd drop-in before networkd starts. |
40 | 43 | networking.usePredictableInterfaceNames = lib.mkForce false; |
41 | 44 | networking.useNetworkd = true; |
42 | 45 | systemd.network.networks."10-eth0" = { |
|
48 | 51 | dhcpV4Config.UseDNS = true; |
49 | 52 | }; |
50 | 53 |
|
51 | | - # Fetch IPv6 config from Hetzner metadata API at boot and apply via networkd drop-in |
52 | 54 | systemd.services.hetzner-ipv6 = { |
53 | 55 | description = "Configure IPv6 from Hetzner Cloud metadata"; |
54 | 56 | wantedBy = [ "network-pre.target" ]; |
|
60 | 62 | }; |
61 | 63 | path = with pkgs; [ |
62 | 64 | curl |
63 | | - jq |
64 | 65 | coreutils |
65 | 66 | gawk |
66 | 67 | ]; |
67 | 68 | script = '' |
68 | 69 | set -euo pipefail |
69 | 70 | METADATA=$(curl -sf http://169.254.169.254/hetzner/v1/metadata) |
70 | 71 |
|
71 | | - # Parse IPv6 address and gateway from the YAML metadata |
72 | 72 | IPV6_ADDR=$(echo "$METADATA" | awk '/type: static/{found=1} found && /address:/{print $2; exit}') |
73 | 73 | IPV6_GW=$(echo "$METADATA" | awk '/type: static/{found=1} found && /gateway:/{print $2; exit}') |
74 | 74 | IPV6_DNS=$(echo "$METADATA" | awk '/type: static/{found=1} found && /dns_nameservers:/{dns=1; next} dns && /^ *-/{print $2; next} dns{exit}') |
|
79 | 79 | fi |
80 | 80 |
|
81 | 81 | mkdir -p /etc/systemd/network/10-eth0.network.d |
82 | | - cat > /etc/systemd/network/10-eth0.network.d/ipv6.conf <<EOF |
83 | | - [Network] |
84 | | - Address=$IPV6_ADDR |
85 | | - DNS=$(echo "$IPV6_DNS" | head -1) |
86 | | - DNS=$(echo "$IPV6_DNS" | tail -1) |
87 | | -
|
88 | | - [Route] |
89 | | - Gateway=$IPV6_GW |
90 | | - Destination=::/0 |
91 | | - EOF |
92 | | -
|
93 | | - # Remove leading whitespace from heredoc |
94 | | - sed -i 's/^[[:space:]]*//' /etc/systemd/network/10-eth0.network.d/ipv6.conf |
| 82 | + { |
| 83 | + echo "[Network]" |
| 84 | + echo "Address=$IPV6_ADDR" |
| 85 | + echo "$IPV6_DNS" | while read -r dns; do |
| 86 | + [ -n "$dns" ] && echo "DNS=$dns" |
| 87 | + done |
| 88 | + echo "" |
| 89 | + echo "[Route]" |
| 90 | + echo "Gateway=$IPV6_GW" |
| 91 | + echo "Destination=::/0" |
| 92 | + } > /etc/systemd/network/10-eth0.network.d/ipv6.conf |
95 | 93 | ''; |
96 | 94 | }; |
97 | 95 |
|
|
150 | 148 | }; |
151 | 149 |
|
152 | 150 | # Tailscale |
153 | | - services.tailscale.enable = true; |
| 151 | + services.tailscale = { |
| 152 | + enable = true; |
| 153 | + useRoutingFeatures = "both"; |
| 154 | + openFirewall = true; |
| 155 | + }; |
154 | 156 |
|
155 | 157 | time.timeZone = "UTC"; |
156 | 158 | } |
0 commit comments