NMState: una herramienta de configuración de red declarativa

Este artículo describe y demuestra NMState, un administrador de red que utiliza un enfoque declarativo para configurar hosts. Esto significa que define el estado de configuración deseado a través de una API y la herramienta aplica la configuración a través de un proveedor.

Enfoques de configuración: imperativo frente a declarativo

La gestión de redes puede ser una tarea muy compleja según el tamaño y la diversidad del entorno. En los primeros días de la TI, la administración de redes se basaba en procedimientos manuales realizados por administradores de redes a través de dispositivos de red. Hoy en día, la Infraestructura como Código (IaC) permite automatizar esas tareas de una manera diferente. Hay, esencialmente, dos enfoques: imperativo o declarativo.

En un enfoque imperativo, define “cómo” llegará al estado de configuración deseado. El paradigma declarativo define “cuál” es el estado de configuración deseado, por lo que no determina qué pasos se requieren ni en qué orden se deben realizar. Actualmente, este enfoque está reuniendo a más expertos y puede encontrarlo en la mayoría de las herramientas de administración y orquestación que se utilizan actualmente.

NMState: una herramienta declarativa

NMState es un administrador de red que le permite configurar hosts siguiendo un enfoque declarativo. Significa que define el estado de configuración deseado a través de una API declarativa hacia el norte y esta herramienta aplica la configuración a través de un proveedor hacia el sur.

Actualmente, el único proveedor admitido por NMState es NetworkManager, que es el servicio principal para abordar las capacidades de red en Fedora linux Sin embargo, el ciclo de vida de desarrollo de NMState agregará otros proveedores gradualmente.

Para obtener más información sobre NMState, visite su proyecto sitio o github repositorio.

Instalación

NMState está disponible en Fedora Linux 29+ y requiere NetworkManager 1.26 o posterior instalado y ejecutándose en el sistema. A continuación se muestra la instalación en Fedora linux34:

$ sudo dnf -y install nmstate
…
output omitted
…
Installed:
  NetworkManager-config-server-1:1.30.4-1.fc34.noarch      gobject-introspection-1.68.0-3.fc34.x86_64      nispor-1.0.1-2.fc34.x86_64              nmstate-1.0.3-2.fc34.noarch              
  python3-gobject-base-3.40.1-1.fc34.x86_64                python3-libnmstate-1.0.3-2.fc34.noarch          python3-nispor-1.0.1-2.fc34.noarch      python3-varlink-30.3.1-2.fc34.noarch  

Complete!

En este punto, puede usar nmstatectl como una herramienta de línea de comandos para NMState. Consulte nmstatectl –help o man nmstatectl para obtener más información sobre esta herramienta.

Uso de NMstate

Comience por verificar la versión de NMState instalada en el sistema:

$ nmstatectl version
1.0.3

Compruebe la configuración actual de una interfaz de red, por ejemplo, la configuración eth0:

$ nmstatectl show eth0
2021-06-29 10:28:21,530 root         DEBUG    NetworkManager version 1.30.4
2021-06-29 10:28:21,531 root         DEBUG    Async action: Retrieve applied config: ethernet eth0 started
2021-06-29 10:28:21,531 root         DEBUG    Async action: Retrieve applied config: ethernet eth1 started
2021-06-29 10:28:21,532 root         DEBUG    Async action: Retrieve applied config: ethernet eth0 finished
2021-06-29 10:28:21,533 root         DEBUG    Async action: Retrieve applied config: ethernet eth1 finished
---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 100
    next-hop-address: ''
    next-hop-interface: eth0
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 100
    next-hop-address: 192.168.122.1
    next-hop-interface: eth0
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 100
    next-hop-address: ''
    next-hop-interface: eth0
    table-id: 254
interfaces:
- name: eth0
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.238
      prefix-length: 24
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    dhcp: true
  ipv6:
    enabled: true
    address:
    - ip: fe80::c3c9:c4f9:75b1:a570
      prefix-length: 64
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    autoconf: true
    dhcp: true
  lldp:
    enabled: false
  mac-address: 52:54:00:91:E4:4E
  mtu: 1500

Como puede ver arriba, la configuración de red muestra cuatro secciones principales:

  • dns-resolver: esta sección tiene la configuración del servidor de nombres para esta interfaz.
  • reglas de ruta: establece las reglas de enrutamiento.
  • rutas: incluye rutas tanto dinámicas como estáticas.
  • Interfaces: esta sección describe la configuración de ipv4 e ipv6.

Modificar la configuración

Puede modificar el estado de configuración deseado en dos modos:

  • Interactivo: editar la configuración de la interfaz a través de nmstatectl edit. Este comando invoca el editor de texto definido por la variable de entorno EDITOR para que el estado de la red se pueda editar en formato yaml. Después de terminar la edición, NMState aplicará la nueva configuración de red a menos que haya errores de sintaxis.
  • basado en archivos: aplicar la configuración de la interfaz mediante nmstatectl apply, que importa un estado de configuración deseado desde un archivo yaml o json creado anteriormente.

Las siguientes secciones le muestran cómo cambiar la configuración de red usando NMState. Estos cambios pueden ser perjudiciales para el sistema, por lo que se recomienda realizar estas tareas en un sistema de prueba o en una máquina virtual invitada hasta que comprenda mejor NMState.

El sistema de prueba en uso aquí tiene dos interfaces Ethernet: eth0 y eth1:

$ ip -br -4 a
lo               UNKNOWN        127.0.0.1/8 
eth0             UP             192.168.122.238/24 
eth1             UP             192.168.122.108/24 

Ejemplo de modo de configuración interactiva:

Change the MTU of eth0 interface to 9000 bytes using the nmstatectl edit command as follows (all changes are in bold):
$ sudo nmstatectl edit eth0

---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 100
    next-hop-address: ''
    next-hop-interface: eth0
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 100
    next-hop-address: 192.168.122.1
    next-hop-interface: eth0
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 100
    next-hop-address: ''
    next-hop-interface: eth0
    table-id: 254
interfaces:
- name: eth0
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.123
      prefix-length: 24
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    dhcp: true
  ipv6:
    enabled: true
    address:
    - ip: fe80::c3c9:c4f9:75b1:a570
      prefix-length: 64
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    autoconf: true
    dhcp: true
  lldp:
    enabled: false
  mac-address: 52:54:00:91:E4:4E
  mtu: 9000

Después de guardar y salir de la edición, NMState aplica el nuevo estado deseado de la red:

2021-06-29 11:29:05,726 root         DEBUG    Nmstate version: 1.0.3
2021-06-29 11:29:05,726 root         DEBUG    Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 102, 'next-hop-address': '', 'next-hop-interface': 'eth0', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 102, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth0', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 102, 'next-hop-address': '', 'next-hop-interface': 'eth0', 'table-id': 254}]}, 'interfaces': [{'name': 'eth0', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.238', 'prefix-length': 24}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'dhcp': True}, 'ipv6': {'enabled': True, 'address': [{'ip': 'fe80::5054:ff:fe91:e44e', 'prefix-length': 64}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'autoconf': True, 'dhcp': True}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:91:E4:4E', 'mtu': 9000}]}
--- output omitted ---
2021-06-29 11:29:05,760 root         DEBUG    Async action: Update profile uuid:2bdee700-f62b-365a-bd1d-69d9c31a9f0c iface:eth0 type:ethernet started
2021-06-29 11:29:05,792 root         DEBUG    Async action: Update profile uuid:2bdee700-f62b-365a-bd1d-69d9c31a9f0c iface:eth0 type:ethernet finished

Ahora, use el comando ip y también el archivo de configuración eth0 para verificar que la MTU de eth0 sea de 9000 bytes.

$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:91:e4:4e brd ff:ff:ff:ff:ff:ff
    altname enp1s0

$ sudo cat /etc/NetworkManager/system-connections/eth0.nmconnection 
[sudo] password for admin: 
[connection]
id=eth0
uuid=2bdee700-f62b-365a-bd1d-69d9c31a9f0c
type=ethernet
interface-name=eth0
lldp=0
permissions=

[ethernet]
cloned-mac-address=52:54:00:91:E4:4E
mac-address-blacklist=
mtu=9000

[ipv4]
dhcp-client-id=mac
dhcp-timeout=2147483647
dns-search=
method=auto

[ipv6]
addr-gen-mode=eui64
dhcp-duid=ll
dhcp-iaid=mac
dhcp-timeout=2147483647
dns-search=
method=auto
ra-timeout=2147483647

[proxy]

Ejemplo de modo de configuración basado en archivos:

Usemos el enfoque basado en archivos para establecer un nuevo estado de configuración. En este caso, deshabilite la configuración de IPv6 en la interfaz eth1.

Primero, cree un archivo yaml para definir el estado deseado de la interfaz eth1. Use nmstatectl show para guardar la configuración actual y luego nmstatectl edit para deshabilitar IPv6. Nuevamente, todos los cambios están en negrita y las eliminaciones se muestran tachadas:

$ nmstatectl show eth1 > eth1.yaml

$ vi eth1.yaml
---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 101
    next-hop-address: 192.168.122.1
    next-hop-interface: eth1
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
interfaces:
- name: eth1
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.108
      prefix-length: 24
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    dhcp: true
  ipv6:
    enabled: false
    address:
    - ip: fe80::5054:ff:fe3c:9b04
      prefix-length: 64
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    autoconf: true
    dhcp: true    
  lldp:
    enabled: false
  mac-address: 52:54:00:3C:9B:04
  mtu: 1500

Después de guardar la nueva configuración, utilícela para aplicar el nuevo estado:

$ sudo nmstatectl apply eth1.yaml

2021-06-29 12:17:21,531 root         DEBUG    Nmstate version: 1.0.3
2021-06-29 12:17:21,531 root         DEBUG    Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 101, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}]}, 'interfaces': [{'name': 'eth1', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.108', 'prefix-length': 24}], 'auto-dns': True, 'auto-gateway': True, 'auto-route-table-id': 0, 'auto-routes': True, 'dhcp': True}, 'ipv6': {'enabled': False}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:3C:9B:04', 'mtu': 1500}]}
--- output omitted ---
2021-06-29 12:17:21,582 root         DEBUG    Async action: Update profile uuid:5d7244cb-673d-3b88-a675-32e31fad4347 iface:eth1 type:ethernet started
2021-06-29 12:17:21,587 root         DEBUG    Async action: Update profile uuid:5d7244cb-673d-3b88-a675-32e31fad4347 iface:eth1 type:ethernet finished
--- output omitted ---
Desired state applied: 
---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 101
    next-hop-address: 192.168.122.1
    next-hop-interface: eth1
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
interfaces:
- name: eth1
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.108
      prefix-length: 24
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    dhcp: true
  ipv6:
    enabled: false
  lldp:
    enabled: false
  mac-address: 52:54:00:3C:9B:04
  mtu: 1500

Puedes comprobar que la interfaz eth1 no tiene configurado ningún IPv6:

$ ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128 
eth0             UP             192.168.122.238/24 fe80::5054:ff:fe91:e44e/64 
eth1             UP             192.168.122.108/24 

$ sudo cat /etc/NetworkManager/system-connections/eth1.nmconnection 
[connection]
id=eth1
uuid=5d7244cb-673d-3b88-a675-32e31fad4347
type=ethernet
interface-name=eth1
lldp=0
permissions=

[ethernet]
cloned-mac-address=52:54:00:3C:9B:04
mac-address-blacklist=
mtu=1500

[ipv4]
dhcp-client-id=mac
dhcp-timeout=2147483647
dns-search=
method=auto

[ipv6]
addr-gen-mode=eui64
dhcp-duid=ll
dhcp-iaid=mac
dns-search=
method=disabled

[proxy]

Aplicando cambios temporalmente

Una característica interesante de NMState le permite configurar temporalmente un estado de red deseado. En caso de que esté satisfecho con la configuración, puede enviarla después. De lo contrario, se revertirá cuando expire el tiempo de espera (el valor predeterminado es 60 segundos).

Modificar la configuración eth1 de la anterior example por lo que tiene una dirección estática IPv4 en lugar de obtenerla dinámicamente por DHCP.

$ vi eth1.yaml

---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 101
    next-hop-address: 192.168.122.1
    next-hop-interface: eth1
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
interfaces:
- name: eth1
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.110
      prefix-length: 24
    auto-dns: true
    auto-gateway: true
    auto-route-table-id: 0
    auto-routes: true
    dhcp: false
  ipv6:
    enabled: false
  lldp:
    enabled: false
  mac-address: 52:54:00:3C:9B:04
  mtu: 1500

Ahora, aplique esta configuración temporalmente usando la opción sin compromiso para que sea válida solo por 30 segundos. Esto se puede hacer agregando la opción –timeout. Mientras tanto, ejecutaremos el comando ip -br a tres veces para ver cómo cambia la dirección IPv4 configurada en la interfaz eth1 y luego se revierte la configuración.

$ ip -br a && sudo nmstatectl apply --no-commit --timeout 30 eth1.yaml && sleep 10 && ip -br a && sleep 25 && ip -br a
lo               UNKNOWN        127.0.0.1/8 ::1/128 
eth0             UP             192.168.122.238/24 fe80::5054:ff:fe91:e44e/64 
eth1             UP             192.168.122.108/24 
2021-06-29 17:29:18,266 root         DEBUG    Nmstate version: 1.0.3
2021-06-29 17:29:18,267 root         DEBUG    Applying desire state: {'dns-resolver': {'config': {}, 'running': {'search': [], 'server': ['192.168.122.1']}}, 'route-rules': {'config': []}, 'routes': {'config': [], 'running': [{'destination': 'fe80::/64', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '0.0.0.0/0', 'metric': 101, 'next-hop-address': '192.168.122.1', 'next-hop-interface': 'eth1', 'table-id': 254}, {'destination': '192.168.122.0/24', 'metric': 101, 'next-hop-address': '', 'next-hop-interface': 'eth1', 'table-id': 254}]}, 'interfaces': [{'name': 'eth1', 'type': 'ethernet', 'state': 'up', 'ipv4': {'enabled': True, 'address': [{'ip': '192.168.122.110', 'prefix-length': 24}], 'dhcp': False}, 'ipv6': {'enabled': False}, 'lldp': {'enabled': False}, 'mac-address': '52:54:00:3C:9B:04', 'mtu': 1500}]}
--- output omitted ---
Desired state applied: 
---
dns-resolver:
  config: {}
  running:
    search: []
    server:
    - 192.168.122.1
route-rules:
  config: []
routes:
  config: []
  running:
  - destination: fe80::/64
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
  - destination: 0.0.0.0/0
    metric: 101
    next-hop-address: 192.168.122.1
    next-hop-interface: eth1
    table-id: 254
  - destination: 192.168.122.0/24
    metric: 101
    next-hop-address: ''
    next-hop-interface: eth1
    table-id: 254
interfaces:
- name: eth1
  type: ethernet
  state: up
  ipv4:
    enabled: true
    address:
    - ip: 192.168.122.110
      prefix-length: 24
    dhcp: false
  ipv6:
    enabled: false
  lldp:
    enabled: false
  mac-address: 52:54:00:3C:9B:04
  mtu: 1500
Checkpoint: NetworkManager|/org/freedesktop/NetworkManager/Checkpoint/7
lo               UNKNOWN        127.0.0.1/8 ::1/128 
eth0             UP             192.168.122.238/24 fe80::5054:ff:fe91:e44e/64 
eth1             UP             192.168.122.110/24 
lo               UNKNOWN        127.0.0.1/8 ::1/128 
eth0             UP             192.168.122.238/24 fe80::5054:ff:fe91:e44e/64 
eth1             UP             192.168.122.108/24 

Como puede ver arriba, la dirección IP de eth1 cambió temporalmente de 192.168.122.108 a 192.168.122.110 y luego volvió a 192.168.122.108 después de que expiró el tiempo de espera.

Conclusión

NMState es una herramienta de configuración de red declarativa que actualmente aplica el estado de configuración de red deseado en un host a través de la API NetworkManager. Este estado se puede definir de forma interactiva con un editor de texto o con un enfoque basado en archivos creando un archivo yaml o json.

Este tipo de herramienta proporciona Infraestructura como código, permite la automatización de tareas de red y también reduce posibles errores de configuración o escenarios de red inestables que podrían surgir al usar métodos de configuración heredados.

Related Posts