tech-ingolf-wagner-de/content/nixos/tinc.md

317 lines
7.9 KiB
Markdown
Raw Normal View History

---
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,
2018-08-15 15:42:33 +02:00
for example forwarding to sub-networks and network wise activation and deactivation.
I designed it to work nicely with
[NixOps](https://nixos.org/nixops/).
You have to `enable` and `disable ` every network you define,
2018-08-15 15:42:33 +02:00
instead of `enable ` tinc which enables all defined networks.
This should make it easy to define all your networks
2018-08-15 15:42:33 +02:00
in one file (to keep track about everything),
and micromanage in the computer specific definitions.
2018-08-15 15:42:33 +02:00
# How to import
2018-08-15 15:42:33 +02:00
You can use `fetchgit` to import it without downloading it yourself.
{{% 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
```
2018-08-15 15:42:33 +02:00
$> cat *.pub > hostfile ; rm *.pub
```
## Key-File-structure for these Examples
2018-08-15 15:42:33 +02:00
In the following examples I expect the following file-structure for keys and config files.
```
2018-08-15 15:42:33 +02:00
|-- public
| |-- Gibson
| | `-- hostfile
| |-- Hackbardt
| | `-- hostfile
| `-- HAL
| `-- hostfile
`-- secrets
|-- Gibson
| |-- ed25519_key.priv
| `-- rsa_key.priv
|-- Hackbardt
| |-- ed25519_key.priv
| `-- rsa_key.priv
`-- HAL
|-- ed25519_key.priv
`-- rsa_key.priv
```
2018-08-15 15:42:33 +02:00
# Connect 3 computers
2018-08-15 15:42:33 +02:00
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.
2018-08-15 15:42:33 +02:00
You can have multiple computers which are reachable from the internet
but for this example we only have one.
{{<figure src="/nixos/tinc/3computers.svg">}}
Here is the `configuration.nix`.
2018-08-15 15:42:33 +02:00
First we define the whole topolgy in `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
2018-08-15 15:42:33 +02:00
publicHostFile = host: fileContent ./public/"${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;
};
};
}
}
```
2018-08-15 15:42:33 +02:00
If we deploy that and check the servers,
we can see that tinc creates interfaces called `tinc.private`.
Observing the routes we see that tinc sets up everything which is needed for proper routing.
```
$Gibson> ip addr show dev tinc.private
4: tinc.private: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 `<computername>.<networkname>`
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!
2018-08-15 15:42:33 +02:00
# Connect 2 sub-networks
2018-08-15 15:42:33 +02:00
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/).
2018-08-15 15:42:33 +02:00
It can be resolved by using the `tincSubnet` parameter,
to configure sub-network routing.
{{<figure src="/nixos/tinc/2subnets.svg">}}
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
2018-08-15 15:42:33 +02:00
publicHostFile = name: fileContent ./public/"${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";
};
};
};
};
}
...
```
2018-08-15 15:42:33 +02:00
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 the other network-nodes.
```
2018-08-15 15:42:33 +02:00
$Gibson> 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.
```
2018-08-15 15:42:33 +02:00
$Hackbardt> 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
```
2018-08-15 15:42:33 +02:00
The module also sets the `sysctl` parameter
`net.ipv4.config.tinc/private.forwarding`
and
`net.ipv6.config.tinc/private.forwarding`
2018-08-15 15:42:33 +02:00
to
make sure the `tinc.private` interface forwards the traffic
2018-08-15 15:42:33 +02:00
to the configured sub-networks.