Univerzitní eduroam pouze na IPv6: CLAT, DHCPv6 a logování

13. 11. 2024
Doba čtení: 11 minut

Sdílet

 Autor: Depositphotos
Na Univerzitě Pardubice jsme se rozhodli nasadit IPv6 only síť na eduroamu. Získané zkušenosti nyní sdílím, protože mi při práci chyběl komplexní návod, který by spojil jednotlivé služby do jednoho řešení.

předchozím článku jsme se zabývali především mechanismy NAT64 a DNS64. Dnes se zaměříme na CLAT, DHCPv6 a logování.

CLAT

CLAT je taková zvláštní věc: překládá IPv4 provoz v klientovi na IPv6, aby ho mohl NAT64 přeložit zpátky na IPv4. Trochu šílené? Ano, ale velmi užitečné.

Představte si, že jste v IPv6-only síti, otevřete si webový prohlížeč a do adresního řádku místo http://www.seznam.cz napište jeho IP adresu http://77.75.79.222. Na IPv4 only síti budete přesměrováni na hlavní stránku seznamu a vše je OK. Ale na IPv6-only síti bez CLAT se nic nestane. Při dotazu jste nepoužili DNS, a tak nemohlo dojít k NAT64 a stránka se nezobrazí. CLAT tak umožňuje fungovat aplikacím, které vyžadují IPv4 adresu či se na ni přímo odkazují, i v IPv6-only síti.

CLAT démon je spuštěn přímo v počítači nebo mobilu a vytvoří lokální rozhraní s IPv4. Spouští se automaticky, když při připojování do sítě najde speciální reverzní zónu. Pozor, opět se nám tu opakuje náš známý IPv6 překladový pool, který jsme použili v ostatních službách.

/etc/bind/named.conf
zone "ipv4only.arpa" {
  type master;
  file "ipv4only.arpa";
  };

/var/cache/bind/ipv4only.arpa
$TTL 86400               ; Default TTL 24 hours
@ IN SOA dns-cache.upce.cz. root.dns-cache.upce.cz. (
         2016052400      ; Serial
         7200            ; Refresh ( 7200 = 2 hours)
         3600            ; Retry   ( 3600 = 1 hour)
         15724800        ; Expire  (15724800 = 6 months)
         60              ; Minimum
         )
@ IN NS  dns.example.net.
@ IN A    192.0.0.170
@ IN A    192.0.0.171
@ IN AAAA 64:ff9b::192.0.0.170
@ IN AAAA 64:ff9b::192.0.0.171

Spuštěný CLAT démon si z tohoto záznamu nakonfiguruje IPv6 překladový pool. Pak vezme veškerý IPv4 provoz, který na něj přijde, a převede ho na IPv6. Ten provoz pak rovnou míří na NAT64 a vše chodí.

Jak poznáme, že je CLAT spuštěný? V konfiguraci sítě uvidíme nastavené IPv4 adresy ze zóny: 192.168.0.170. Tak nám funguje test přístupu na IP adresu, jak jsem psal na začátku kapitoly. Bohužel CLAT je přítomen jen v zařízeních od Apple a v Androidech. Linux ani Windows jej nemají – abych byl úplně přesný: nemají takový, který se automaticky aktivuje, když zjistí IPv6-only síť.

Bez CLAT je problém s některými IPv4-only VPN službami, právě o tom byla má přednáška na Dni IPv6. Pokud se jedná o split tunnel, VPN se vám sice krásně připojí, i skrz NAT64. Ale pak vám nastaví svoje vlastní DNS, které neumí DNS64 a služby, které potřebují NAT64, přestanou fungovat.

Poznámka

Nás zajímal CLAT, který běží v klientovi, ale existuje i možnost samostatného externího CLAT routeru. Je to jedna ze zmiňovaných funkcionalit Jool. Máte IPv4 síť připojenou do IPv6-only sítě. První Jool dělá CLAT router a druhý NAT64 router.

DHCPv6

Kea nahrazuje původní DHCP server od ISC, nově se objevila v Debianu 12. Je to úplně přepracovaný koncept s novými vlastnostmi. Funguje HA replikace lease tabulky, lease tabulku i konfiguraci DHCP poolů můžete mít v databázi atd. Bohužel konfigurační soubor je ve formátu JSON, což se ručně dost špatně upravuje a je nutné před každým restartem služby vždy zkontrolovat konfiguraci.

Kea je modulární, například DHCPv4, DHCPv6 a DDNS jsou samostatní démoni. Control agent je základní démon, který umí ostatní konfigurovat, ale hlavně monitorovat. Používá REST API a spouští si webový server na lokálním portu 8000. Pozor, my budeme dělat HA, takže je třeba výchozí port TCP 8000 změnit.

/etc/kea/kea-ctrl-agent.conf
{"Control-agent": {
    "http-host": "127.0.0.1",
    "http-port": 8001,
...

DHCP démon má samostatný konfigurační soubor. Jak vidíte, je velmi snadné v něm zapomenout závorku či čárku, důsledně kontrolujte konfiguraci před restartem ( kea-dhcp6 -t /etc/kea/kea-dhcp6.conf). Konfigurační soubor je dlouhý, proto budu jeho výpis přerušovat komentáři.

/etc/kea/kea-dhcp6.conf
{
"Dhcp6": {
"interfaces-config": {
  "interfaces": [ "eth0/2000:1000::2", "vrrp.6/2000:1000::10" ]
  },
"control-socket": {
  "socket-type": "unix",
  "socket-name": "/run/kea/kea6-ctrl-socket"
  },
"multi-threading": {
"enable-multi-threading": true,
"thread-pool-size": 6,
"packet-queue-size": 64
},
"lease-database": {
  "type": "memfile",
  "lfc-interval": 3600
  },
...

V první řadě je třeba, aby Kea poslouchala na správných IP adresách. Kea je pravděpodobně dělaná tak, aby ve výchozím nastavení poskytovala adresy klientům, kteří jsou s ní ve stejném L2 segmentu. Když dáte do rozhraní jen eth0, Kea poslouchá jen na lokální linkové adrese a multicastové adrese pro DHCPv6. Ale my potřebujeme, aby poslouchala na globální unicastové adrese eth0 a rozhraní vrrp.6, proto tam vyplníme rozhraní i s adresami.

Na primárním serveru je to v pohodě, ale na záložním serveru rozhraní vrrp.6 nemá IP adresu a Kea se nespustí. Jenže ona musí běžet i na záložním serveru, aby se synchronizovala lease tabulka. Jak z toho ven?

Použil jsem notifikační skript z keepalive démona. Když se záložní server stane primárním, spustí se skript, který pomocí řádkového editoru sed upraví konfiguraci a přidá rozhraní vrrp.6. Při přechodu do backup módu zase rozhraní odebere. K čemu je logIPv6mac.timer, si probereme v sekci logování.

/etc/keepalived/master.sh
#!/bin/bash
sed -e 's/\("interfaces": \[ "eth0\/2000:1000::2"\) \]/\1,"vrrp.6\/2000:1000::10" \]/' /etc/kea/kea-dhcp6.conf > /tmp/kea-dhcp6.conf
cp /tmp/kea-dhcp6.conf /etc/kea/kea-dhcp6.conf
systemctl restart kea-dhcp6-server.service
systemctl start logIPv6mac.timer

/etc/keepalived/backup.sh
#!/bin/bash
sed -e 's/\("interfaces": \[ "eth0\/2000:1000::2"\).*\]/\1 \]/' /etc/kea/kea-dhcp6.conf > /tmp/kea-dhcp6.conf
cp /tmp/kea-dhcp6.conf /etc/kea/kea-dhcp6.conf
systemctl restart kea-dhcp6-server.service
systemctl stop logIPv6mac.timer

Zkoušel jsem i elegantnější způsob, který se používá na HA systémech. Kernelový parametr ip_nonlocal_bind, viz. článek na cybercity.biz, by měl umožnit aplikacím, aby poslouchali i na rozhraních, které nemají IP adresu. Ale tento způsob nefungoval, rozhraní VRRP nemá lokální linkovou adresu a Kea se proto nespustí. Domnívám se, že konfigurace skriptem je tedy jediný možný způsob.

Poznámka

Ve verzi IPv4 to funguje, Kea umí poslouchat na rozhraní, které nemá adresu. Když se ta adresa na rozhraní objeví, lze na ni komunikovat. Vychází to asi z principu, jak DHCPv4 pracuje.

Teď zpátky k naší konfiguraci. Multi-threading je ve výchozí konfiguraci vypnutý. Kvůli zvýšení výkonu DHCP serveru ho používám, spouštím šest vláken. Záleží totiž na počtu CPU v serveru.

Seznam leasů je v souboru, ale lze používat i databázi. Jenže pak už do HA nastupuje databázová replikace a konfigurace takového robustního řešení může být docela hustá. Kea má velmi zajímavé vlastnosti, podívejte se do její dokumentace. Přišlo mi to jako přejít od prvňáčkových polámaných pastelek k paletě s olejovými barvami.

...
"expired-leases-processing": {
  "reclaim-timer-wait-time": 3600,
  "hold-reclaimed-time": 172800,
  "max-reclaim-leases": 0,
  "max-reclaim-time": 0
  },
"hooks-libraries": [
  { "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_lease_cmds.so" },
  { "library": "/usr/lib/x86_64-linux-gnu/kea/hooks/libdhcp_ha.so",
    "parameters": {
      "high-availability": [ {
        "this-server-name": "primary.example.net",
        "mode": "hot-standby",
        "max-response-delay": 60000,
        "max-ack-delay": 5000,
        "max-unacked-clients": 5,
        "sync-timeout": 60000,
        "multi-threading": {
          "enable-multi-threading": true,
          "http-dedicated-listener": true,
          "http-listener-threads": 0,
          "http-client-threads": 0
          },
        "peers": [
          {
          "name": "primary.example.net",
          "url": "https://[2000:1000::2]:8000/",
          "trust-anchor": "/etc/kea/ca.pem",
          "cert-file": "/etc/kea/primary-crt.pem",
          "key-file": "/etc/kea/primary-key.pem",
          "role": "primary"
          },
          {
          "name": "backup.example.net",
          "url": "https://[2000:1000::3]:8000/",
          "trust-anchor": "/etc/kea/ca.pem",
          "cert-file": "/etc/kea/backup-crt.pem",
          "key-file": "/etc/kea/backup-key.pem",
          "role": "standby"
          }
          ]
        } ]
      }
    }
  ],
"renew-timer": 21600, #clients should renew every 6h
"rebind-timer": 32400, #clients should start looking for other servers after 9h
"preferred-lifetime": 40300,
"valid-lifetime": 43200, #leases will be valid for 12h
...

Rozšíření funkcionality se dělá pomocí hooků – modulů, které se musí zapnout. HA je jeden z velké řady modulů. Konfigurace není složitá, nakonfigurujeme peery a určíme, který server je který pomocí jejich hostnamů. V URL serveru jsem použil IP adresu, server má více adres a já si nebyl jistý, kterou Kea použije.

Synchronizační přenosy jsou HTTP, lze je šifrovat pomocí TLS. Certifikáty mohou být z interní CA a s dlouhou platností. Je trochu zvláštní, že každý server potřebuje mít také kompletní certifikát svého protějšku.

Povšimněte si ukončovacích závorek v konfiguraci HA, je jich tam velké množství. To je přesně to místo, kde jsem měl velké potíže je správně doplnit, pozor na to. JSON není formát dobrý pro ruční úpravy.

Pak následuje sekce, kde určujeme, jak často si má klient IP adresy obnovovat a jak dlouho platí.

...
"option-data": [
  { "name": "dns-servers", "data": "2000:1000::10" },
  { "name": "domain-search", "data": "example.net" }
  ],

"subnet6": [
  { "subnet": "2000:1000:a::/64",
    "pools": [ { "pool": "2000:1000:a::11-2000:1000:a:ffff:ffff:ffff:ffff" } ]
    },
  { "subnet": "2000:1000:b::/64",
    "pools": [ { "pool": "2000:1000:b::11-2000:1000:b:ffff:ffff:ffff:ffff" } ]
  }
],
"loggers": [
  {
  "name": "dhcpd",
  "output_options": [
    {
    "output": "syslog",
    "pattern": "%-5p %m\n"
    }
  ],
  "severity": "INFO",
  "debuglevel": 9
    }
  ]
}
}

Konečně se dostáváme ke konfiguraci poolů, ta je velmi jednoduchá. Option-data jsou společné parametry pro oba pooly, v každém poolu určíme první a poslední použitelnou adresu a máme hotovo.

Poznámka

Po dlouhodobém používání jsem zjistil v HA jistou nestabilitu, není to nic vážného. Asi tak jednou ročně se master zblázní a stane se backupem. Jenže backup zůstane backupem a služba jako celek přestane dávat adresy. Synchronizační zprávy chodí, v logu žádná hláška. Řešení je zastavit obě služby, včetně keepalived a pak služby opět spustit. Monitoring, pomocí Kea control agenta, je proto velmi důležitý.

Monitorování stavu služby uděláme pomocí curl dotazu na Kea agenta. Integrace s monitoringem pomocí Zabbix agenta nebo NRPE agenta tak není problém. Kea agent umí také poslouchat na adrese rozhraní a být tak dosažitelný i mimo server. Ale nenašel jsem, že by uměl použít TLS, volám ho tedy jen lokálně.

curl -s -X POST -H "Content-Type: application/json" -d '{ "command": "status-get", "service": [ "dhcp6" ] }' http://127.0.0.1:8001 | jq

[
  {
    "arguments": {
      "high-availability": [
        {
          "ha-mode": "hot-standby",
          "ha-servers": {
            "local": {
              "role": "primary",
              "scopes": [
                "primary.example.net"
              ],
              "state": "hot-standby"
            },...

Dalším užitečným příkazem je aktuální výpis lease tabulky.

curl -s -X POST -H "Content-Type: application/json" -d '{ "command": "lease6-get-all", "service": [ "dhcp6" ] }' http://127.0.0.1:8001 | jq -r '.[].arguments.leases[] | [."ip-address",."hw-address"] | @tsv'

Logování

Jako provozovatel eduroamu máme ze zákona povinnost uchovávat jeden rok logy. Musíme být, v případě kybernetického incidentu, schopni říci, který uživatel používal v danou dobu kterou IP adresu. Z Jool logu získáme IPv6 adresu uživatele, pokud použil NAT64. Pokud ne, jdeme do DHCPv6 logu či logu autokonfigurace. Tím dostaneme MAC zařízení, podle které dohledáme uživatele v Radiusu z přihlášení pomocí 802.1X.

Je úplně jedno, jestli je MAC adresa anonymizovaná, či se mění při každém připojení (například iPad má pro každé uložené připojení vygenerovanou vlastní MAC adresu), z principu musí použitá MAC odpovídat autorizaci a vazbě na IP adresu v tabulce sousedů.

Jool loguje do syslogu (tady jsem se ve své přednášce zmýlil, když jsem mluvil, že je k logování potřeba conntrackd) a těch logů je značné množství. Určitě doporučuji zapnout logování session, jen základní bib log neukládá TCP/UDP port.

session log
2023-03-22 07:16:11 kernel: [681095.182606] Jool: default 2023/3/22 6:16:11 (GMT) - Added session 2000:1000:a:0:cddc:d5ca:9745:d6ca#52350|64:ff9b::55ef:4517#443|12.15.168.197#18630|85.239.69.23#443|TCP
2023-03-22 07:20:27 kernel: [681350.908121] Jool: default 2023/3/22 6:20:27 (GMT) - Forgot session 2000:1000:a::cddc:d5ca:9745:d6ca#52350|64:ff9b::55ef:4517#443|12.15.168.197#18630|85.239.69.23#443|TCP

bib log
2023-03-22 07:40:52 kernel: [682576.228180] Jool: default 2023/3/22 6:40:52 (GMT) - Mapped 2000:1000:a:0:59f:34e1:7a1c:88c4#59797 to 12.15.168.215#4410 (TCP)
2023-03-22 07:45:09 kernel: [682832.671547] Jool: default 2023/3/22 6:45:9 (GMT) - Forgot 2000:1000:a:0:59f:34e1:7a1c:88c4#59797 to 12.15.168.215#4410 (TCP)

/etc/rsyslog.d/jool.conf
if $msg contains 'Jool: default' then /var/log/jool.log
if $msg contains 'Jool: default' then @serverNaArchivaciLogu

/etc/rsyslog.d/kea.conf
if $programname contains "kea" and $msg contains "leases" then @serverNaArchivaciLogu

Logování adres získaných pomocí autokonfigurace, a jejich spárování s MAC adresou zařízení, je trochu oříšek. Při řešení jsem vycházel z tabulky sousedů, která má u zařízení Cisco výchozí trvanlivost čtyři hodiny. Pokud někdo bude komunikovat skrz router, jeho IP adresa zůstane v tabulce sousedů čtyři hodiny. Když si každou hodinu tabulku sousedů stáhnu a uložím, získám párování IPv6-MAC, ať adresu získal jakkoliv. Vlastně se tady projeví i staticky nakonfigurovaná adresa. Mě zajímají jen adresy, které komunikovaly. Adresa, která nekomunikovala, asi nebude součástí kybernetického incidentu.

Skript je psaný v Perlu, protože je z doby, kdy byl tenhle jazyk ještě sexy. Dnes by měl být v Pythonu, ale proč přepisovat něco, co funguje. IPv6 adresa je zakódovaná do OID a MAC adresa je její hodnota. Tím, že musím stahovat celou tabulku sousedů, způsobím v tu dobu značné vytížení CPU routeru.

#!/usr/bin/perl -w
my $router_ip="xxxxxxx";
my $router_comm="xxxxxxx";

use Net::SNMP;
use Data::Dumper;
use strict;
use Sys::Syslog;

openlog("logIPv6mac",,"local7");

my $snmp=Net::SNMP->session(Hostname=>$router_ip,Community=>$router_comm,Version=>"v2c");
if(!defined($snmp)) {exit 3;}

my $data=$snmp->get_table(".1.3.6.1.2.1.4.35.1.4");#ipNetToPhysicalPhysAddress
foreach my $oid (keys %{$data}) {
  my $mac;
  my $ipv6;
  if($oid=~/\.1\.3\.6\.1\.2\.1\.4\.35\.1\.4\..*(32\.1\.7\.24\.6\.3\.16\.[\d\.]*)$/) {
    my @ipv6d=split(/\./,$1);
    for(my $i=0;$i<@ipv6d;$i+=2) {
      $ipv6.=sprintf("%x",$ipv6d[$i])."".sprintf("%02x",$ipv6d[$i+1]).":";
      }
    chop($ipv6);
    $mac=$data->{$oid};
    $mac=~s/([0-9a-f]{2})/\:$1/g;
    $mac=~s/0x\:([0-9a-f\:]{17})/$1/g;
    syslog("info","IPv6 to MAC: $ipv6 - $mac");
    }
  }
$snmp->close;

closelog();

Tento skript funguje jen proti routerům Cisco, ale bylo by možné jej přepsat na přihlášení přes SSH a poslání příkazu. Tak by se dal použít asi i na jiných platformách. Berte ho spíše pro inspiraci.

/etc/systemd/system/logIPv6mac.service
[Unit]
[Service]
Type=oneshot
ExecStart=/usr/local/bin/logIPv6mac.pl
[Install]
WantedBy=multi-user.target

/etc/systemd/system/logIPv6mac.timer
[Unit]
[Timer]
OnCalendar=*-*-* *:00,30:00
[Install]
WantedBy=timers.target

Skript spouštím pomocí časovače systemd, proto ho na VRRP backup serveru pomocí notifikačního skriptu deaktivuji.

ict ve školství 24

Poznámka

Řešení s vyčítáním tabulky sousedů má jednu potenciální díru, jak mě upozornil na konferenci kolega Jaroslav Zdeněk z ČVUT. Uživatel se připojí, komunikuje a odpojí se. Ihned potom se připojí někdo jiný a nastaví si IP adresu předchozího uživatele. Tím přepíše na routeru tabulku sousedů a znehodnotí původní párování. Vlastně by se to, v rámci autokonfigurace, mohlo stát i samovolně. Doporučuje použít mnohem bezpečnější, ale také náročnější, řešení – IP accounting.

To je ke konfiguraci vše. I když se obávám, že na použití IPv6 only sítí ještě nedozrál čas. Důvodem jsou IPv4 VPN a neexistence CLATu na mém desktopu, který by je ošetřil.

(Autorem obrázků je Jan Vondráček.)

Autor článku

V současné době pracuje jako správce linuxových systémů na Univerzitě Pardubice.