In July 2019, an AS I manage was born. As long as I worked on the deployment of the network and its services, I tried many ways to generate the IPv6 addresses for each services. SLAAC, simple IPv6 addresses, based on the hostname and random ones.
SLAAC addresses are OK but requires using EUI-64 to get a stable IPv6 address and it has the drawback of exposing the MAC address and potentially permits vendor-specific attacks. It came with another problem when virtualization or containers (LXC) is used. As I would like to push even further Infrastructure as a Code (IaC) on our infrastructure with tools like Terraform, it would require more work to get the IPv6 address of the VM/container.
Simple IPv6 addresses like 2001:db8:cafe::1
or 2001:db8:dc1::dead:beef
are common addresses and are easily targeted by network scans and attacks. We mainly use this approach on border routers and for anycast routing, not for unicast services.
Random static IPv6 addresses or based on the hostname “solve” the problem I mentioned about IaC by assigning a static IPv6 address to a container or a VM. Generating an IPv6 address based on the hostname was for me a pretty nice and easy way to generate IPv6 addresses and also have the advantage of containing a fragment of the hostname in case we have an IPv6 address not in the IPAM or a reverse DNS record. With the time, I also used this method for the network at home.
2020, the premises#
In 2020, I decided to write a small script in Python called v6gen that helped us to generate IPv6 addresses based on this approach. The way it worked was very simple, it took in parameter a IPv6 subnet and a FQDN. The script take the hostname part from the FQDN, convert it in hexadecimal. If the hostname doesn’t fit, it removes the vowels and progressively remove last characters to let the hostname fit.
$ v6gen 2001:db8:beef:cafe::/64 -n myserver.blah.net
2001:db8:beef:cafe:6d79:7365:7276:6572/128
If the script made pretty much the job, some features like generating the ARPA version of the IP was missing and unfortunately I never improved the script to expose this info. For the domain name of the AS, we use Gandi LiveDNS and so it’s possible for us to quickly create a record via the API. For our rDNS zones, we deployed Knot servers but we don’t have some sort of APIs to manage our zones from scripts and automation tools. Instead, we need to convert the IP generated by v6gen and add it to our zones and deploy them via Ansible.
2022, ipg is born!#
By the end of 2021, I wanted to rewrite entirely the script to be more feature-complete and also to play with Go. Unfortunately, time and motivation was not there and I simply created a task on my ToDo list for one day. In August 2022, I wanted to rework on the project, motivated like never to finish it. I took what I started earlier and worked on what I called ipg
for… IP generator. Great name heh?
ipg
is built with few great libraries like Kong, a CLI parser, c-robinson/iplib for IP addresses parsing and manipulation and net from the Go standard library.
It’s released under MIT license and available on GitHub. In addition of releasing binaries for pretty much all operating systems and architectures, I also published a tiiiinyy Docker image that weight only 5.72MB, thanks to the usage of Busybox as base image! It can be pulled with the command:
docker pull ghcr.io/themimitoof/ipg
This time I wanted to make the tool more flexible, add missing features from v6gen
and make it usable and understandable by machines. Let take a look on how it works:
$ ipg --help
Usage: ipg <subnet>
A simple IPv6 generator for lazy netadmins.
Arguments:
<subnet> IPv6 Subnet
Flags:
-h, --help Show context-sensitive help.
-r, --random Generate a random IPv6 address on the given subnet.
-n, --name="hostname" Specify the hostname of a machine, an IPv6 address will be generated based on it.
-s, --silent Only display values without their labels.
-f, --format="console" Specify the type of output. Possible values: console, json
-a, --address Display the generated IP address.
-R, --reverse Display the ARPA version of the IP address.
-d, --dns Returns a DNS record ready to paste to a DNS zone.
-x, --rrecord Returns a rDNS record ready to paste to a DNS zone.
-t, --ttl=86400 TTL value for DNS returned DNS records.
To generate an IPv6 address based on a hostname/FQDN, nothing changed compared to v6gen
except returned information:
$ ipg -n hello 2001:db8:beef::/64
IP address: 2001:db8:beef::68:656c:6c6f
Reverse IP address: f.6.c.6.c.6.5.6.8.6.0.0.0.0.0.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa
DNS record: hello 86400 IN AAAA 2001:db8:beef::68:656c:6c6f
ARPA DNS record: f.6.c.6.c.6.5.6.8.6.0.0.0.0.0.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa. 86400 IN PTR hello
In case you don’t want to generate an IPv6 address based on a hostname/FQDN, you can also generate a random one with the -r
flag:
$ ipg -r 2001:db8:beef::/64
IP address: 2001:db8:beef:0:7ca:edea:8ae4:65e9
Reverse IP address: 9.e.5.6.4.e.a.8.a.e.d.e.a.c.7.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa
DNS record: hostname 86400 IN AAAA 2001:db8:beef:0:7ca:edea:8ae4:65e9
ARPA DNS record: 9.e.5.6.4.e.a.8.a.e.d.e.a.c.7.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa. 86400 IN PTR hostname
By default, the IPv6 address, the ARPA version and DNS records are returned. In case you only want to get the IPv6 address and the PTR record, you can use filters:
$ ipg -axn hello 2001:db8:beef::/64
IP address: 2001:db8:beef::68:656c:6c6f
ARPA DNS record: f.6.c.6.c.6.5.6.8.6.0.0.0.0.0.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa. 86400 IN PTR hello
There are few filters available:
-s, --silent
: It removes the label before each value and the colors of each returned values-a, --address
: returns the generated IPv6 address-R, --reverse
: returns the ARPA version of the generated IPv6 address-d, --dns
: returns a BIND compatible record ready to be pasted into a Bind zone-x, --rrecord
: returns a BIND compatible PTR record ready to be pasted into a ARPA zone
The -s
filter can be useful if you want to use ipg
in a bash
script, like a VM deployment script.
$ ipg -san hello 2001:db8:beef::/64
2001:db8:beef::68:656c:6c6f
If you want a random IPv6 address and have ready to copy-pasta DNS records, you can mix the -r
flag and the -n
one:
$ ipg -rn toto.foo.bar 2001:db8:beef::/64
IP address: 2001:db8:beef:0:9962:7058:c2a7:996
Reverse IP address: 6.9.9.0.7.a.2.c.8.5.0.7.2.6.9.9.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa
DNS record: toto.foo.bar. 86400 IN AAAA 2001:db8:beef:0:9962:7058:c2a7:996
ARPA DNS record: 6.9.9.0.7.a.2.c.8.5.0.7.2.6.9.9.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa. 86400 IN PTR toto.foo.bar
In case you would like to use other languages like Python, Ruby, for scripting purposes or for example creating an Ansible or Puppet module, you change the format of the command to output in a JSON format with the --format json
or -f json
argument:
$ ipg -f json -n hello 2001:db8:beef::/64 | jq
{
"hostname": "hello",
"ip_addr": "2001:db8:beef::68:656c:6c6f",
"arpa_addr": "f.6.c.6.c.6.5.6.8.6.0.0.0.0.0.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa",
"dns_record": "hello 86400 IN AAAA 2001:db8:beef::68:656c:6c6f",
"ptr_record": "f.6.c.6.c.6.5.6.8.6.0.0.0.0.0.0.0.0.0.0.f.e.e.b.8.b.d.0.1.0.0.2.ip6.arpa. 86400 IN PTR hello"
}
Note: Using the JSON
format don’t take in consideration the given filters.
For Go projects, you can even import ipg
and use the exposed methods on your projects. The API documentation is available on pkg.go.dev.
Conclusion#
ipg
probably will not fit the needs of any other network in the world than the AS I maintain and my home network. But in case you start using it on your network, star the project on GitHub and shoot me a message on Twitter, I will be happy to know that ipg
is used elsewhere 😉.
If you see a potential on ipg
and you would like to see new features that match with your needs and you think that can be used more broadly, feel free to contribute to the code or discuss with me by opening a discussion on GitHub.