--- title: "Tinc" date: 2018-07-07T21:43:24+02:00 tags: - NixOS - NixOps - Tinc --- In this article I will describe how to use my [NixOS](https://nixos.org/) tinc-module. I had to write my own, because the standard `services.tinc` module misses a lot of features, for example maintaining sub-net and network wise activation and deactivation. I designed it to be used with [NixOps](https://nixos.org/nixops/). You have to `enable` and `disable ` every network you define, instead of `enable ` tinc and which enables all defined networks. This should make it easy to define all your networks in one file (to keep track about everything). # How to use To use this module you can use `fetchgit` to import it. {{% note %}} To find the newest `rev` and `sha256` just call `nix-shell -p nix-prefetch-git --run "nix-prefetch-git https://github.com/mrVanDalo/nixos-tinc.git"` {{% /note %}} ``` {pkgs, ... }: let tincModule = { url = "https://github.com/mrVanDalo/nixos-tinc.git"; rev = "8755d954fcadeef5d0e30488a7b11e3f1a505769"; sha256 = "0swkk5zxg9vqdf2j0m9zki13wr0g8ws77y4v5wzklrdcmbny5qjm"; }; in { imports = [ "${tincModule}" ]; } ``` Now you are ready to go! # Create Keys First of all, we have to create tinc keys for every computer in the VPN Mesh. To put the keys in the current folder we use the `'--config .'` option, ``` $> nix-shell -p tinc_pre --run "tinc --config . generate-keys 4096" ``` After that is done we create the `hostfile` by ``` $> cat *.pub > hostfile ``` ## Key-File-structure for these Examples In the following examples we use the following file-structure ``` |-- configuration.nix `-- secrets |-- Gibson | |-- ed25519_key.priv | |-- hostfile | `-- rsa_key.priv |-- Hackbardt | |-- ed25519_key.priv | |-- hostfile | `-- rsa_key.priv `-- HAL |-- ed25519_key.priv |-- hostfile `-- rsa_key.priv ``` # Using tinc to connect 3 computers First we want to connect 3 computers in a private network of range ``10.1.1.0/24``. One computer needs to be accessable from the internet, it will be the computer that connects all the other computer. You can have multiple computers which are reachable but for this example we only have one. {{
}} Here is the `configuration.nix`. First we setup the whole infrastructure in the `default` and than we `enable` and configure secret-keys for every computer in `Gibson`, `Hackbardt` and `HAL` ``` let includePrivateKeys = host: { deployment.keys."rsa_key" = { keyFile = ./secrets/"${host}"/rsa_key.priv; destDir = "/root/secrets"; }; deployment.keys."ed25519_key" = { keyFile = ./secrets/"${host}"/ed25519_key.priv; destDir = "/root/secrets"; }; }; in { # for all machines # ---------------- default = {config, pkgs, lib, ... }: { with lib; services.custom.tinc = let publicHostFile = host: fileContent ./secrets/"${host}"/hostfile; in { "private" = { debugLevel = 0; port = 655; networkSubnet = "10.1.1.0/24"; hosts = { Gibson = { realAddress = [ "my.awesome.dns.com" ]; tincIp = "10.1.1.1"; publicKey = publicHostFile "Gibson"; }; Hackbardt = { tincIp = "10.1.1.2"; publicKey = publicHostFile "Hackbardt"; }; HAL = { tincIp = "10.1.1.3"; publicKey = publicHostFile "HAL"; }; }; }; }; } # Gibson specific # --------------- Gibson = {config, pkgs, ... }: includePrivateKeys "Gibson" // { services.custom.tinc = { "private" = { enable = true; privateRsaKeyFile = config.deployment.keys."rsa_key".path; privateEd25519KeyFile = config.deployment.keys."ed25519_key".path; }; }; } # Hackbardt specific # ------------------ Hackbardt = {config, pkgs, ... }: includePrivateKeys "Hackbardt" // { services.custom.tinc = { "private" = { enable = true; connectTo = [ "Gibson" ]; privateRsaKeyFile = config.deployment.keys."rsa_key".path; privateEd25519KeyFile = config.deployment.keys."ed25519_key".path; }; }; } # HAL specific # ------------ HAL = {config, pkgs, ... }: includePrivateKeys "HAL" // { services.custom.tinc = { "private" = { enable = true; connectTo = [ "Gibson" ]; privateRsaKeyFile = config.deployment.keys."rsa_key".path; privateEd25519KeyFile = config.deployment.keys."ed25519_key".path; }; }; } } ``` If we deploy that and check the servers, we can see tinc creates interfaces called `tinc.private`. Observing the routes we see that tinc sets up everything so these computers can see each other. ``` $Gibson> ip addr show dev tinc.private 4: tinc.private: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 3f:ac:bd:c2:f6:9c brd ff:ff:ff:ff:ff:ff inet 10.1.1.1/32 scope global tinc.private valid_lft forever preferred_lft forever inet 169.254.22.60/16 brd 169.254.255.255 scope global tinc.private valid_lft forever preferred_lft forever inet6 ffa1::2afc:b2ff:fcf2:f97a/64 scope link valid_lft forever preferred_lft forever ``` ``` $Gibson> ip route show dev tinc.private 10.1.1.0/24 scope link 169.254.0.0/16 proto kernel scope link src 169.254.22.60 metric 204 ``` It also creates `/etc/host` entries `.` so you don't have to remember the IPs. ``` $Gibson> ping HAL.private -c 1 PING HAL.private (10.1.1.3) 56(84) bytes of data. 64 bytes from HAL.private (10.1.1.1): icmp_seq=1 ttl=64 time=5.27 ms ``` **Awesome!** That was easy! # Using tinc to connect 2 sub-nets So far so good, but lets imagine we have some virtual machines running on 2 computers and want to make these virtual machines see each other. This is a very common problem in [Kubernetes](https://kubernetes.io/). It can be resolved by using the `tincSubnet` parameter. {{
}} Achieving this is very simple, just add the `tincSubnet` parameter in the `hosts` attribute and your done. ``` ... default = {config, pkgs, lib, ... }: { with lib; services.custom.tinc = let publicHostFile = name: fileContent ./secrets/"${name}"/hostfile; in { "private" = { debugLevel = 0; port = 655; networkSubnet = "10.1.1.0/24"; hosts = { Gibson = { realAddress = [ "my.awesome.dns.com" ]; tincIp = "10.1.1.1"; publicKey = publicHostFile "Gibson"; }; Hackbardt = { tincIp = "10.1.1.2"; subnetIp = "10.2.2.0/24"; publicKey = publicHostFile "Hackbardt"; }; HAL = { tincIp = "10.1.1.3"; subnetIp = "10.2.3.0/24"; publicKey = publicHostFile "HAL"; }; }; }; }; } ... ``` After deployment we can see that `Gibson` has proper routing to the configured `tincSubnet` ranges as well as to `10.1.1.0/24` to reach other the computers. ``` $> ip route show dev tinc.private 10.1.1.0/24 scope link 10.2.2.0/24 scope link 10.2.3.0/24 scope link 169.254.0.0/16 proto kernel scope link src 169.254.116.112 metric 203 ``` `Hackbardt` has routing to the network provided by `HAL`, but has no routing (on the `tinc.private` interface) to the network it provides it self. ``` $> ip route show dev tinc.private 10.1.1.0/24 scope link 10.2.2.0/24 scope link 169.254.0.0/16 proto kernel scope link src 169.254.116.112 metric 203 ``` The module also sets the `sysctl` parameter `net.ipv4.config.tinc/private.forwarding` and `net.ipv6.config.tinc/private.forwarding` to make sure the `tinc.private` interface forwards the traffic to the configured sub-nets. {{% note %}} If that is not set to true, you have to turn it on yourself. In the future this will also be managed by the `module`. {{% /note %}}