/ cloud

Customizing Digitalocean Floating IPs

Digitalocean allows, for free, the option to add an additional IP address to your virtual machine ("droplet"). These floating IPs are similiar to Amazon AWS Elastic IPs and are tied to your account and location, rather than any specific instance.

This means that when an instance is destroyed, you can still preserve the floating address and migrate it to your new instance, providing it's in the same datacenter location. Floating IPs also have load balancing features, where if a site/instance goes down, it can automatically redirect to a backup instance.

It is for these reasons it is recommended you use floating IPs for public services

This is a write up of me attempting to tinker around with them.

How do they work

When you add a floating IP, your droplet gains an additional IP address in the 10.0.0.0/8 private range, known as the anchor IP.

Traffic to the floating IP is sent to the droplet's anchor IP

If you have services listening on 0.0.0.0 (wildcard) then they will be available on both the instance and the floating address. I recommend binding management services to only to the instance IP and binding public services to the floating IP

By default, your floating IP is not used as a source address for the droplets egress traffic.

Using the API to find the IPs

You can find your normal (canonical) address, anchor address, and any attached floating addresses through the droplet api available at 169.254.169.254

×
-
+
Konsole

[root@droplet]:~# wget -qO- 169.254.169.254/metadata/v1/floating_ip/ipv4/active
true
[root@droplet]:~# wget -qO- 169.254.169.254/metadata/v1/floating_ip/ipv4/ip_address
203.0.113.55
[root@droplet]:~# wget -qO- 169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/address
10.10.0.7
[root@droplet]:~# wget -qO- 169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address
192.0.2.199


Running Different web servers on different addresses.

As you can see, it's simply a matter of selectively binding to one of the addresses; obviously don't bind to the wildcard.

Nginx on the floating address

  listen 10.10.0.7:443 http2 ssl; 
  server_name floating.example.com;

Lighttpd on the canonical address

server.socket               = "192.0.2.199:443"
server.username             = "www-data"

Using the floating address as your source address

×
-
+
Konsole

[root@droplet ~]# wget -qO- 169.254.169.254/metadata/v1/interfaces/public/0/anchor_ipv4/gateway
10.10.0.1
[root@droplet ~]# dig @208.67.222.222 myip.opendns.com
192.0.2.199
[root@droplet ~]# ip r add to 208.67.222.222 via 10.10.0.1 dev eth0
[root@droplet ~]# dig @208.67.222.222 myip.opendns.com
203.0.113.55


Locking down access to the instance address

As mentioned earlier, external connections are routed by default through the instance address, rather than the floating address. (You don't see the floating address when you go to whats-my-ip)

We can, however, use the gateway in the anchor network instead. This will make the instance IP address invisible to the wider internet. Connections will be dropped because the droplet no longer has a route, so we add a static route to a few trusted networks.

Finding the BGP announced route (if you don't know it)

lookup_bgp_route() {
  host -t txt $(awk -F. '{print $4"."$3"."$2"."$1}'<<<$1).origin.asn.cymru.com  | cut -d\" -f2 | awk -F " | " '{print $3}'
};

# to get the unternet root of the current ssh client
# lookup_bgp_root ${SSH_CLIENT%% *}`

Adding your static routes and changing the defaut route to the anchor

×
-
+
Konsole

[root@droplet: ~]# homeroute=198.51.6.0/24
[root@droplet: ~]# apiprefix="//169.254.169.254/metadata/v1/interfaces/public/0"

[root@droplet: ~]# ip r add $homeroute via $(curl $apiprefix/ipv4/gateway)
[root@droplet: ~]# ip r change default via $(curl $apiprefix/anchor_ipv4/gateway) dev eth0
[root@droplet: ~]# ip r save >/etc/ip.routes.$(date +%s)


/etc/network/interfaces

To persist it across boots

auto eth0
iface eth0 inet static
        address $anchorip
        netmask 255.255.0.0
        gateway $anchorgw
        up ip -4 address add $instanceip dev eth0
        up ip -4 route add to $homeroute via $instancegw src $instanceip

So now my instance IP is not reachable by default, unless I add a route. Note, this isn't a substitute for firewalling, and for very sensitive services like ssh, I strongly recommend locking it down via the cloud firewall.