Débugguez vos règles firewall NSX-T

Quand on commence à faire de la production sur NSX-T (tout comme sur d’autres systèmes sécurisés), il est plus qu’utile de disposer d’outils de troubleshooting fiables et « temps réels ». Même si VMware Log Insight fait partie des systèmes de logging recommandés avec NSX-T, difficile au départ de tout maîtriser et souvent, un bon petit « tail » sur un log en fichier texte est finalement un moyen efficace et assez simple de debugguer et de vérifier ce qui se passe. Après une session extrêmement intéressante avec le support NSX-T de VMware (merci en particulier à Anaëlle qui se reconnaitra, si elle passe par là), je vous propose de partager les découvertes que j’ai fait concernant le debugguing des policies/règles DFW et Edge sur NSX-T.

Le préalable à ce debugging consiste à activer le logging sur les règles firewall que vous souhaitez checker. pour ce faire, il faut aller dans l’interface NSX-T, onglet « Security », vous placer sur la règle en question et enfin commuter le paramètre « log » logging sur « Yes » :

Une fois fait, à chaque fois que cette règle « match », quel que soit le contexte, une trace est envoyée au système de logging adéquat. Si votre règle fait partie des politiques de micro-segmentation – elles sont donc opérées par le Distributed Firewall de NSX-T – les traces sont envoyées dans le fichier /var/log/dfwpktlogs.log de l’ESXi sur lequel s’exécute la VM qui a déclenché le « match » à l’instant T. Si par contre, la règle fait partie de vos règles Nord-Sud – donc dépendantes de votre Service Router associé au TIER0/TIER1 concerné – dans ce cas, c’est dans le /var/log/syslog du edge-node actif où tourne votre routeur.

Dans les deux cas, les traces en questions vous indiquent le numéro de la règle qui a matché, l’ip source, l’ip destination et les ports TCP/UDP associés. Pour ESXi ça donne un truc comme ça :

[root@monbelesxi:/var/log] cat dfwpktlogs.log
2019-08-01T14:15:01.549Z c70cc550 INET match PASS 7202 OUT 48 TCP 10.167.192.12/41402->X.X.X.X/443 S
2019-08-01T14:15:01.688Z c70cc550 INET match PASS 7202 OUT 48 TCP 10.167.192.12/46182->X.X.X.X/443 S
2019-08-01T14:15:22.281Z c70cc550 INET TERM 7202 OUT TCP FIN 10.167.192.12/46182->X.X.X.X/443 16/21 1736/20679
2019-08-01T14:15:26.284Z c70cc550 INET TERM 7202 OUT TCP FIN 10.167.192.12/41402->X.X.X.X/443 17/13 1933/7295
2019-08-01T14:31:16.481Z c70cc550 INET match PASS 7202 OUT 48 TCP 10.167.192.12/41272->X.X.X.X/443 S
2019-08-01T14:31:39.142Z c70cc550 INET TERM 7202 OUT TCP FIN 10.167.192.12/41272->X.X.X.X2/443 13/14 1299/8286
2019-08-01T14:33:44.467Z c70cc550 INET match PASS 7202 OUT 48 TCP 10.167.192.12/41278->X.X.X.X/443 S
2019-08-01T14:34:06.273Z c70cc550 INET TERM 7202 OUT TCP FIN 10.167.192.12/41278->X.X.X.X/443 13/14 1299/8286
2019-08-01T14:49:22.906Z c70cc550 INET match PASS 7193 OUT 48 UDP 10.167.192.12/43745->10.167.192.8/53
2019-08-01T14:49:53.108Z c70cc550 INET TERM 7193 OUT UDP 10.167.192.12/43745->10.167.192.8/53 4/2 260/270
2019-08-01T14:50:18.408Z c70cc550 INET match PASS 7193 OUT 48 UDP 10.167.192.12/34059->10.167.192.8/53
(...)

L’id de la règle matchée apparait juste après le « match PASS » ou le « TERM ». Dans l’exemple, on discerne la 7202 et la 7193. Vous remarquerez au passage que ce format de log est relativement standard, il est facile de l’injecter dans une suite type Elastic/Logstash/Kibana par exemple (si vous ne voulez pas utiliser Log Insight). Pour retrouver le nom de votre règle, il faut aller dans la section « Advanced Networking & Security->Security->Distributed Firewall » et déployer vos règles, Vous aurez accès à leur ID. Malheureusement, jusqu’à la 2.4.1, a ma connaissance, l’ID n’est visible qu’à cet endroit dans l’interface graphique :

Pour la partie Edge, vu que c’est un peu noyé dans le syslog général, il faut filtrer tout cela :

root@nsxedgeprd1:/var/log# cat syslog | grep FIREWALL
<181>1 2019-08-01T14:22:31.088Z nsxedgeprd1 NSX 6172 FIREWALL [nsx@6876 comp="nsx-edge" subcomp="datapathd.firewallpkt" level="INFO"] <7bfa47e10d33a47d0:b0c1d913389ca296> INET reason-match DROP 7173 IN 60 TCP 10.167.192.12/54340->94.23.44.83/22 S
<181>1 2019-08-01T14:23:03.120Z nsxedgeprd1 NSX 6172 FIREWALL [nsx@6876 comp="nsx-edge" subcomp="datapathd.firewallpkt" level="INFO"] <7bfa47e10d33a47d0:b0c1d913389ca296> INET reason-match DROP 7173 IN 60 TCP 10.167.192.12/54340->94.23.44.83/22 S
<181>1 2019-08-01T14:25:39.876Z nsxedgeprd1 NSX 6172 FIREWALL [nsx@6876 comp="nsx-edge" subcomp="datapathd.firewallpkt" level="INFO"] <7bfa47e10d33a47d0:b0c1d913389ca296> INET reason-match DROP 7173 IN 60 TCP 10.167.192.12/54344->94.23.44.83/22 S
<181>1 2019-08-01T14:25:46.890Z nsxedgeprd1 NSX 6172 FIREWALL [nsx@6876 comp="nsx-edge" subcomp="datapathd.firewallpkt" level="INFO"] <7bfa47e10d33a47d0:b0c1d913389ca296> INET reason-match DROP 7173 IN 60 TCP 10.167.192.12/54344->94.23.44.83/22 S
(...)

La encore pour obtenir le nom de la règle à partir de son ID, rendez-vous « Advanced Networking & Security->Security->Routers », cliquez sur le routeur concerné et placez-vous dans l’onglet « Services->Edge firewall ». Oui, ok, c’est un peu lourd, mais je pense que ça devrait s’arranger avec les futures versions de NSX-T… on ne sait jamais, peut-être dès la 2.5 qui devrait sortir incessamment à l’heure où j’écris ces lignes (1er Août 2019) :

Vous l’aurez compris, un concentrateur de log est particulièrement utile pour ce genre de traces, mais si vous voulez allez vite à la source de l’information, cela peut vous aider :)

D’autre part, j’ai également découvert, via cette session, de nouvelles commandes sur ESXi directement en lien avec la gestion des règles NSX-T (mais aussi NSX-V) mais aussi le suivi des flow IP, d’une manière plus générale.

La première, summarize-dvfilter vous permet d’obtenir, pour chaque VM tournant sur la machine, la liste de ses « slots » dvFilter (si ça vous intéresse de creuser cette notion, rendez-vous ici sur un excellent billet de VMware qui explique la chaine « IO network » sur ESXi entre la VM et la carte réseau de l’hyperviseur). Par exemple, un petit extract de cette commande sur une de nos machines de production :

[root@monsuperesxi:/var/log] summarize-dvfilter | grep -A 16 linuxtst
world 57328518 vmm0:linuxtst vcUuid:'50 13 ea 2b cc 9d e3 7b-a9 86 ec 26 c7 fc bf 60'
 port 67108885 linuxtst.eth0
  vNic slot 2
   name: nic-57328518-eth0-vmware-sfw.2
   agentName: vmware-sfw
   state: IOChain Attached
   vmState: Attached
   failurePolicy: failClosed
   serviceVMID: 3
   filter source: Dynamic Filter Creation
  vNic slot 12
   name: nic-57328518-eth0-vmware-si.12
   agentName: vmware-si
   state: IOChain Attached
   vmState: Detached
   failurePolicy: failOpen
   serviceVMID: none
   filter source: Dynamic Filter Creation

Ce qui nous intéresse ici c’est le nom des dvFilters implantés sur la VM « linuxtst ». Vous pouvez voir ici que le slot « 2 » est actuellement attaché, c’est celui qu’on va utiliser pour la suite. Une fois votre nom de slot trouvé, vous pouvez l’utiliser avec d’autres commandes tout aussi intéressantes. Par exemple, la commande « vsipioctl getflows -f nom_de_votre_slot » vous affiche les flux IP actuellement référencés sur la VM. Exemple :

[root@monsuperesxi:/var/log] vsipioctl getflows -f nic-57328518-eth0-vmware-sfw.2
Count retrieved from kernel active=8, inactive=0, drop=0
606778ec00001e16 Active tcp 0800 OUT 7202 1 3 (S,est) 10.167.192.12:Unknown(60849) -> 94.23.44.83:http(80) 1024 EST:EST  rtt 0 0 80 0 2
606778ec00001e49 Active tcp 0800 IN 7198 1 3 (est) 172.27.101.1:Unknown(59536) -> 10.167.192.12:ssh(22) 64240 EST:EST  rtt 24310 9676 12982 73 55
606778ec00001e4a Active udp 0800 OUT 7193 1 3  10.167.192.12:Unknown(45332) -> 10.167.192.8:domain(53)  106 142 1 2
606778ec00001e4b Active tcp 0800 IN 7198 1 3 (est) 172.27.101.1:Unknown(59537) -> 10.167.192.12:ssh(22) 64240 EST:EST  rtt 9168 3496 7106 23 25
606778ec00001e4c Active udp 0800 OUT 7193 1 3  10.167.192.12:Unknown(42260) -> 10.167.192.8:domain(53)  106 142 1 2
606778ec00001e4d Active udp 0800 OUT 7193 1 3  10.167.192.12:Unknown(51467) -> 10.167.192.8:domain(53)  329 110 1 2
606778ec00001e4e Active icmp 0800 OUT 7204 0 0  10.167.192.12 -> 216.58.215.35 8 0 504 504 6 6
606778ec00001e4f Active udp 0800 OUT 7193 1 3  10.167.192.12:Unknown(44287) -> 10.167.192.8:domain(53)  368 144 1 2

Vous pouvez aussi récupérer l’ensemble des règles bas-niveau implantées sur le slot :

[root@ monsuperesxi:/var/log] vsipioctl getrules -f nic-57328518-eth0-vmware-sfw.2
ruleset mainrs {
  # generation number: 0
  # realization time : 2019-08-01T14:57:53
  rule 7228 at 1 inout protocol any from any to any with attribute profile 6d8de4da-b95f-4097-8c41-83c46a34531a accept with log;
  rule 7201 at 2 inout protocol any from any to addrset 38ca6a50-6eaf-45dd-8fa5-197c82e462f4 accept;
  rule 7198 at 3 inout protocol any from addrset 38ca6a50-6eaf-45dd-8fa5-197c82e462f4 to any accept;
  rule 7223 at 4 inout protocol icmp from addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b to any accept;
  rule 7223 at 5 inout protocol ipv6-icmp from addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b to any accept;
  rule 7224 at 6 inout protocol icmp from any to addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b accept;
  rule 7224 at 7 inout protocol ipv6-icmp from any to addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b accept;
  rule 7232 at 8 inout protocol any from addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b to addrset 01f5c758-3a71-45f7-8f0b-1a98c136dd6e accept;
  rule 7204 at 9 inout protocol icmp from addrset 8de38119-bc27-4c8d-b949-88ea24f41bba to any accept;
  rule 7204 at 10 inout protocol ipv6-icmp from addrset 8de38119-bc27-4c8d-b949-88ea24f41bba to any accept;
  rule 7205 at 11 inout protocol icmp from any to addrset 8de38119-bc27-4c8d-b949-88ea24f41bba accept;
  rule 7205 at 12 inout protocol ipv6-icmp from any to addrset 8de38119-bc27-4c8d-b949-88ea24f41bba accept;
  rule 7193 at 13 inout protocol udp from addrset 8de38119-bc27-4c8d-b949-88ea24f41bba to addrset rdst7193 port 123 accept with log;
  rule 7193 at 14 inout protocol udp from addrset 8de38119-bc27-4c8d-b949-88ea24f41bba to addrset rdst7193 port 53 accept with log;
  rule 7193 at 15 inout protocol tcp from addrset 8de38119-bc27-4c8d-b949-88ea24f41bba to addrset rdst7193 port 53 accept with log;
(...)
  rule 7206 at 25 inout protocol udp from addrset 1f18d325-3743-438a-bc21-a5436d5b3429 to any port 53 accept;
  rule 7206 at 26 inout protocol tcp from addrset 1f18d325-3743-438a-bc21-a5436d5b3429 to any port 53 accept;
  rule 7202 at 27 inout protocol tcp from addrset cdf88f5c-c761-4aae-97ae-b28621c86917 to any port 443 accept with log;
  rule 7202 at 28 inout protocol tcp from addrset cdf88f5c-c761-4aae-97ae-b28621c86917 to any port 80 accept with log;
  rule 7221 at 29 inout protocol any from addrset cdf88f5c-c761-4aae-97ae-b28621c86917 to any accept with log;
  rule 7200 at 30 inout protocol udp from addrset 5bd044ab-4a36-4f8a-a472-bd4aadd03e86 to addrset b1fac4b2-ef19-4df2-aa5d-6cc27d145eec port 514 accept;
  rule 7200 at 31 inout protocol tcp from addrset 5bd044ab-4a36-4f8a-a472-bd4aadd03e86 to addrset b1fac4b2-ef19-4df2-aa5d-6cc27d145eec port 514 accept;
  rule 7200 at 32 inout protocol tcp from addrset 5bd044ab-4a36-4f8a-a472-bd4aadd03e86 to addrset b1fac4b2-ef19-4df2-aa5d-6cc27d145eec port 601 accept;
  rule 7212 at 33 inout protocol any from addrset 2ce12eb9-b527-4194-985e-4d418f14c5b4 to any drop with log;
  rule 7213 at 34 inout protocol any from any to addrset 2ce12eb9-b527-4194-985e-4d418f14c5b4 drop with log;
  rule 7215 at 35 inout protocol any from addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b to any accept with log;
  rule 7214 at 36 inout protocol any from any to addrset 317808f6-5d2c-42e1-abed-d729f0c19c0b accept with log;
  rule 7186 at 37 inout protocol any from any to any drop;
  rule 2 at 38 inout protocol any from any to any accept;
}

ruleset mainrs_L2 {
  # generation number: 0
  # realization time : 2019-08-01T14:57:53
  rule 1 at 1 inout ethertype any stateless from any to any accept;
}

Vous noterez ici que les règles macros que l’on peu être amené à mettre en place sur NSX sont éclatées par protocole et doublés pour IPv4 et IPv6 (si vous avez laissé cette option activé dans les règles en question). Ca permet de pouvoir vérifier que toutes ces règles sont bien légitimes et que vous n’avez pas une règle parasite matchant cette VM alors qu’elle ne devrait pas. Bref, encore un excellent moyen de débugguer un flux capricieux ou un pb de sécurité sur une machine.

Les GUID des sets d’addresses (IP, Mac) peuvent être retrouvés avec la même commande vsipioctl mais avec la directive getaddrsets (merci à Alexis pour le rappel) :

[root@monsuperesxi:/var/log] vsipioctl getaddrsets -f nic-57328518-eth0-vmware-sfw.2
addrset 1f18d325-3743-438a-bc21-a5436d5b3429 {
ip 10.167.192.8,
ip 10.167.192.88,
mac 00:50:56:93:75:c6,
mac 00:50:56:93:f8:2c,
}
(...)
addrset 5bd044ab-4a36-4f8a-a472-bd4aadd03e86 {
ip 10.167.192.11,
ip 10.167.200.11,
mac 00:50:56:93:df:02,
}
addrset 8de38119-bc27-4c8d-b949-88ea24f41bba {
ip 10.167.192.8,
ip 10.167.192.9,
(...)
mac 00:50:56:93:73:34,
mac 00:50:56:93:75:c6,
}
(...)
}

Au passage, ça m’a permit de retrouver une règle qui prenait un peu ses aises et matchait des VMs non souhaitées, celle correspondant à l’ID 7228…

Avec tout cela et sans l’aide de VMware Log Insight (cela fera l’objet d’un autre billet sans doute), vous êtes déjà capable à la main, à l’ancienne, de vous dépatouiller de soucis d’accès ou de non accès à certaines VMs en production, voir d’identifier des trous dans votre sécurité, que ce soit vis à vis de la microsegmentation (DFW) ou sur les Service Routers de vos TIERx.