Frameworks para Bash: ¿realmente los necesitamos? Una mirada profunda a bash-it

Después de muchos años 📅de usar bash es normal ir acumulando alias, funciones, variables, scripts, qué ponemos por aquí y por allá, con suerte en algún directorio más o menos estandar. Pero llega un momento que es difícil de manejar todo eso. De acuerdo a mi experiencia he notado que muchas veces se usa algo porque se sacó de un foro de reddit, stackoverflow, de una búsqueda en google o DuckDuckGo, de una respuesta de a un prompt en la IA... o porque alguien lo dijo. Funciona, pero no se tiene ni idea de por qué funciona.

Organización de nuestro bash profesional

Una vez en un curso un alumno me dijo:

- ¡Ahora entiendo porque hago lo que hago cada día en mi trabajo! 😀

Es imposible saber y acordarse de todo, pero tener una idea conceptual al menos general de como funcionan las cosas nos ayuda en el trabajo diario.

Por eso es importante tener en cuenta algunos conceptos de bash fundamentales.

Alias

Los alias se usan para abreviar o cambiar el comportamiento de comandos simples o repetitivos.

Por ejemplo:

alias rm ='rm -i'

De este modo, estamos cambiando el comportamiento del comando rm para que nos pregunte antes de borrar archivos. Hay otras situaciones en los cuales queremos hacer un comando mucho más sencillo:

alias dma='date +%d-%m-%y'

Funciones

Si bien los alias son muy útiles, tienen limitaciones:

❌ No se expanden de manera predeterminada en scripts.
❌ No soportan parámetros posicionales.
❌ Tienen un soporte muy estrecho para lógica condicional.

Si tenemos alguno de esos requerimientos entonces usaremos funciones, como la siguiente que sirve para ver un archivo sin líneas vacías o con "#":

clrfile () 
{ 
    if [[ $# -eq 0 ]]; then
        echo "Error: Proporciona al menos un archivo." 1>&2;
        return 1;
    fi;
    local file;
    for file in "$@";
    do
        if [[ ! -f "$file" ]]; then
            echo "Error: '$file' no es un archivo válido." 1>&2;
            continue;
        fi;
        grep -Ev '^[[:space:]]*#|^$|^[[:space:]]*;' "$file";
    done
}

Variables

A medida que vamos usando bash, encontramos la necesidad de crear variables, por ejemplo:

HISTFILESIZE=1048576
export nikola="/home/sergio/Documentos/Arts/nikola-env"
export GTK_USE_PORTAL=1

Opciones de bash

Bash posee muchas opciones que nos sirven para modificar el comportamiento del shell como vemos a continuación:

shopt -s histappend
shopt -s autocd
shopt -s dirspell
shopt -s cdspell
shopt -s cdable_vars

Con el paso del tiempo, nuestro archivo .bashrc, puede hacerse muy largo, poco claro, poco modular, o hasta incluso creamos muchos archivitos aquí y allá con funciones y/o alias como si fueran scripts. Con suerte tal vez los pongamos en ~/bin o ~ /.local/bin. Cuando llegamos a este punto tal vez estemos necesitando que nuestras herramientas estén organizadas, que manejen estándares, que podamos contar con módulos que se puedan habilitar o deshabilitar para poder administrar y/o desarrollar de manera más profesional. Es decir, necesitamos un framework.

bash-it, un framework como aliado

El shell zsh usa un framework llanado "Oh My Zsh". Sin embargo, las distribuciones principales, a excepción de Kali, el resto usa bash de manera predeterminada. Así que en muchos casos no tiene sentido invertir tiempo en cambiarse a zsh 😄. Y en particular para los que quieran aprender Linux, recomiendo que comiencen con Bash, luego si tienen tiempo y el ámbito corporativo se los permite podrán explorar Zsh como una excelente alternativa.

Por lo tanto, ¿qué tenemos para bash? En un principio encontré que justamente existe: Oh My Bash. Este proyecto usa el popular pero no por eso recomendado método de instalación desde el punto de vista de la seguridad que consiste en "curl-pipe-script". Sin embargo, ciertos problemas de Instalación manual con OMB, me llevaron a considerar a bash-it por tener mejores métricas:

Estas estadísticas hacen que más allá del bonito sitio que armó la comunidad de Oh My Bash eligiera bash-it:

Comarativas de repos bash-it y Oh My Basg

✅ ¿Es técnicamente confiable Bash-it para producción?

No vamos a encontrar a bash-it en repositorios de las distribuciones principales, sin embargo desde un punto de vista técnico puro, Bash-it es bastante seguro porque:

✔️ No requiere privilegios especiales (se instala en el home del usuario).
✔️ Usa código abierto auditable (podés revisar cada plugin, alias, función).
✔️ Es fácil de activar/desactivar funcionalidades individuales (plugins, alias, temas).
✔️ Está diseñado para usarse solo en entornos interactivos, no impactando scripts automáticos.

Sin embargo, la confiabilidad depende mucho del criterio de quien lo administra.

🔐 ¿Consideraciones de seguridad para entorno corporativo?

⚠️ Es código abierto no revisado oficialmente por las distribuciones más populares, por lo cual debés auditar lo que habilitás.
⚠️ Debe implementarse con criterio, evitando plugins que hagan conexiones externas automáticas o definan alias potencialmente riesgosos.
⚠️ Es aconsejable versionar y usar un fork corporativo que controles.

🚨 ¿Riesgos o precauciones a considerar?

❌ Podrías introducir complejidad innecesaria si el equipo no está acostumbrado a Bash avanzado.
❌ Posibilidad de romper comandos estándar con alias mal elegidos.
❌ Necesidad de capacitación interna sobre su uso para evitar malentendidos.

💡 Recomendaciones profesionales (cómo hacerlo confiable):

📌 Usá un fork interno auditado por el equipo de seguridad o infraestructura.
📌 Instalá solo plugins y funcionalidades que realmente necesitás.
📌 No habilites funciones o plugins "experimentales" o desconocidos.
📌 Documentá claramente qué está habilitado y por qué.

Hechas las aclaraciones en las que me siento obligado a hacer, vayamos al procedimiento de instalación 😄.

Instalación de bash-it

Clonamos el repositorio y ejecutamos el script de instalación:

 git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
 ~/.bash_it/install.sh

El script nos va a preguntar si queremos mantener el archivo ~/.bashrc o sobrescribir el archivo y crear un backup (comportamiento por default) o bien agregarlo al final de ~/.bashrc.

Instalación de bash-it

¿Dónde guardamos nuestras configuraciones?

Sea que necesitemos agregar nuestras propias configuraciones o rescatarlas del archivo de backup ~/.bashrc.bak es importante comprender donde tenemos que guardar cada cosa, para eso contamos la siguiente tabla:

Configuración Ubicación
Alias ~/.bash_it/aliases/custom.aliases.bash
Funciones ~/.bash_it/lib/custom.bash
Opciones de shell ~/.bash_it/lib/custom.bash
Variables ~/.bash_it/lib/custom.bash
Autocompletados completion/custom.completion.bash
Funciones
agrupadas
plugins/custom.plugins.bash
Temas custom/themes/mi-tema/mi-tema.theme.bash

¿Dónde colocar nuestros scripts?

Aquí, abro un paréntesis, ya que es importante que entendamos que si contamos con scripts más complejos, lo recomendable es que esté en algún directorio de nuestro path tal como ~/.local/bin. No sería conveniente sobrecargar el entorno, es decir, usarlo con bash-it. Si estamos en duda podemos responder algunas de estas preguntas, frente a un determinado script:

✔️ ¿Tienen su propio #!/bin/bash?

✔️ ¿Son ejecutables y no deben ser cargados en el entorno de bash?

✔️ ¿Pueden usarse desde cualquier shell?

✔️ ¿Podrían ser usados por otros scripts o tareas automatizadas?

✔️ ¿Dependen del contexto del entorno interactivo?

Si la respuesta para una o más a estas preguntas es afirmativa entonces lo mejor es excluirlo de bash-it.

Gestión de alias de bash-it

Podemos ver los alias habilitados con el comando siguiente:

bash-it help aliases

Ese comando nos va a mostrar en principio, los alias clasificados en dos categorías: general y custom. Si queremos ver los alias que tenemos disponibles, ejecutamos:

bash-it help aliases | less

Si queremos habilitar los alias para ansible

bash-it enable alias ansible
ansible enabled with priority 150.
cat .bash_it/enabled/150---ansible.aliases.bash 
# shellcheck shell=bash
about-alias 'ansible abbreviations'

alias ans=ansible
alias ap=ansible-playbook      

En el ejemplo de arriba podemos inspeccionar los alias activados.

¿Cómo ver y activar plugins?

Las funciones son un grupo de funciones relacionados un tema.

bash-it show plugins | fgrep "[x]"
base                 [x]        miscellaneous tools

bash-it enable plugin fzf

⚠ A veces es necesario ejecutar bash-it restart para que aplique un cambio, por ejemplo la eliminación de un alias.

¿Cómo activar temas?

Bash-it cuenta con una gran cantidad de temas para darle un aspecto visual al shell

ls ~/.bash_it/themes/
90210            candy                        elixr                  liquidprompt  nwinkler                pure          slick
agnoster         clean                        emperor                luan          nwinkler_random_colors  purity        standard
atomic           codeword                     envy                   mairan        oh-my-posh              radek         tonka
axin             cooperkid                    essential              mbriggs       p4helpers.theme.bash    rainbowbrite  tonotdo
bakke            cupcake                      font                   metal         parrot                  ramses        tylenol
barbuk           demula                       gallifrey              minimal       pete                    rana          wanelo
base.theme.bash  dos                          githelpers.theme.bash  modern        powerline               redline       zitron
binaryanomaly    doubletime                   gitline                modern-t      powerline-multiline     rjorgenson    zork
bira             doubletime_multiline         hawaii50               modern-time   powerline-naked         robbyrussell
bobby            doubletime_multiline_pyonly  inretio                morris        powerline-plain         roderik
bobby-python     dulcie                       iterate                n0qorg        powerturk               sexy
brainy           duru                         kitsune                newin         primer                  simple
brunton          easy                         lambda                 norbu         pro                     sirup

Para cambiar un tema hay que editar una variable y reiniciar bash-it. Buscar en ~./.bashrc y cambiarla por el tema que nos guste (el predetermindo es bobby):

export BASH_IT_THEME='bobby'

🧩 ¿Entonces qué significa que Bash-it es un framework?

Significa que Bash-it provee un entorno estructurado y modular para personalizar tu shell Bash, con:

Elemento del framework ¿Qué ofrece?
📁 Estructura de directorios Carpetas organizadas (aliases/, plugins/, completion/, themes/)
⚙️ Convenciones Nombres de archivos (custom.aliases.bash, custom.plugins.bash)
🧰 Herramientas Comandos como bash-it enable, bash-it reload
📦 Contenido reutilizable Alias, funciones, completions y temas listos para usar o modificar
🔌 Modularidad Activar o desactivar componentes sin tocar .bashrc directamente
🚀 Extensibilidad Agregás tus propios módulos sin romper la base (custom/ y enabled/)

🧪 Comparación simple

Sin framework (bash "a mano") Con Bash-it
.bashrc lleno de alias y funciones Alias y funciones separados por módulo
No hay forma de desactivar cosas fácilmente bash-it disable alias docker
No hay temas de prompt Temas configurables (BASH_IT_THEME=...)
Todo depende de cómo lo escribas vos Hay convenciones y helpers

🔧 Troubleshooting

La mejor manera de descargar falsos positivos es instalar bash-it en contenedor podman o docker. Una vez que lo probamos allí y descartamoos que haya algo inherente a bash-it o al propio contendor. Luego podemos probarlo en la máquina que tiene el problema creando un usuario de prueba y realizar las siguientes pruebas.

Comparación de archivos de configuración

diff -u /home/sergio/.bashrc /home/test/.bashrc
diff -u /home/sergio/.bash_profile /home/test/.bash_profile

Verificar múltiples cargas de un determinado componente

Por ejemplo, si tenemos sourceado fzf en otro lugar además del plugin de fzf, tendrá problemas al hacer autocompletados de rutas inexistentes, y bash se colgará.

grep -r fzf ~/.bash*

Deshabilitar bash-it temporalmente

cp ~/.bashrc ~/.bash.bash-it
cp ~/.bashrc.bak ~/.bashrc
source `~/.bashrc`

Luego se puede realizar el proceso inverso si estamos en condiciones de volver a cargar bash-it.

✅ Conclusión

Bash-it un framework es decir que no es solo una colección de scripts, sino una forma organizada, modular y extensible de gestionar tu entorno de Bash, con convenciones y herramientas que te permiten escalar y mantener esa personalización de forma profesional.

Nos puede proporcinar un salto de calidad en el uso de bash. Por supuesto, ningún software es perfecto. Muchos plugins por ejemplo, tienen funcionalidades obsoletas o que no existen más. Y recordar que bash-it puede ser confiable en producción, siempre que:

✔️ Sea auditado y validado por tu equipo.
✔️ Se mantenga un control estricto sobre lo que se habilita.
✔️ Se provea capacitación a los usuarios.

Si no estás dispuesto a mantener estos controles internos, entonces sería más seguro no utilizarlo y seguir con .bashrc tradicionales o scripts controlados internamente.

De todas maneras esas tres condiciones: ¿no deberían aplicarlse a cualquier herramienta que se quiera adoptar?

Por otro lado, probar bash-it me sirvió para revisar configuraciones que venía acumulando con los años, muchas de ellas, las eliminé y otras las mejoré para adoptar las mejores prácticas de bash. Indudablemente un sysadmin experimentado podrá probablemente prescindir de bash-it, y usar una configuración ajustada a sus necesidades. Y ese es el aporte de bash-it, hacernos ver que podemos tener una configuración mucho más modular y ordenada.

¿Cuándo fue la última vez que verificaste tu entorno de shell?

🔗 Recursos recomendados para aprovechar a fondo Bash

🔖 Bash Manual (GNU)
La referencia canónica de Bash. Leé esto si querés entender cómo y por qué funciona Bash, no solo qué comandos usar.

🔖 Repositorio oficial de Bash-it
Tu punto de partida para transformar Bash en un entorno potente, modular y personal.

🔖 The Bash Hackers Wiki
Recurso mantenido por la comunidad técnica, con explicaciones brillantes sobre los aspectos más sutiles del shell.

🔖 Greg's Wiki – Bash Pitfalls
Una lista imprescindible de errores comunes en Bash, explicados por uno de los gurús del scripting shell.

🔖 Checks – ShellCheck Wiki
Una guía didáctica, profunda y sin concesiones. Ideal para quienes ya programan pero quieren escribir Bash de verdad. ShellCheck es una extraordinaria herramienta para verificar la calidad de nuestrs scrits y configuracines de bash. Aquí podemos consultar los SC codes,

Introducción a nftables y familia de direcciones

Historia e Introducción

Antes de iptables y nftables, existía ipfwadm, una herramienta que se utilizaba en las primeras versiones del kernel de Linux para gestionar el firewall. Aunque ipfwadm sentó las bases para el firewalling en Linux, fue reemplazada debido a su limitada capacidad de manejo de tráfico y escalabilidad. Primero fue sustituida por ipchains y más tarde por el popular iptables.

nftables

Comunidad y Grupos Clave

  • Netfilter Project: La comunidad principal detrás de iptables y nftables. Este grupo ha sido esencial en el desarrollo, mantenimiento y promoción de herramientas de filtrado de paquetes en Linux.

  • Kernel Contributors: Muchos desarrolladores de kernel han colaborado en la optimización de las capacidades de firewalling en Linux, asegurando su integración fluida y eficiente.

Creación de nftables

nftables fue introducido en 2014 como el sucesor de iptables, diseñado para abordar las limitaciones de su predecesor en términos de rendimiento y escalabilidad.

Si bien eso fue hace más de 10 años, hay cambios tecnológicos que van por ascensor y otros por escalera. En ámbitos corporativos el mero hecho de reemplazar un software que es cardinal en una infraestructura, no es una tarea trivial. Ese cambio se debe a que los tiempos de vida de las distribuciones que las medianas y grandes organizaciones tienen soporte a largo a plazo. A veces es falta de urgencia y otras veces por sencilla negligencia :smile:.

Sin embargo apenas 3 datos para considerar la adopción de nftables:

  • RHEL9 considera iptables, ipset y ebtables herramientas depreciadas.

  • iptables no estará disponible en RHEL10 ni en OpenShift 4.16.

  • Debian Bullseye (a la fecha oldstable) ha depreciado iptables en favor de nftables.

¿Cuál es el problema de iptables?

Aunque ha sido una herramienta extraordinaria que ha servido de base para muchas soluciones de firewall, iptables presenta algunas limitaciones. A continuación, mencionamos tres de las más importantes:

  1. Reprocesamiento completo al modificar reglas:
    Cada vez que se agrega o elimina una regla, el kernel debe volver a procesar toda la lista de reglas. Este enfoque resulta ineficiente, especialmente en entornos donde se realizan cambios frecuentes o se manejan firewalls con un gran número de reglas.

  2. Gestión complicada de rangos de direcciones IP:
    Manejar rangos de direcciones IP o grandes conjuntos de direcciones en iptables puede ser tedioso y poco práctico. La herramienta ipset fue una solución ingeniosa para optimizar esta tarea, ya que permite manejar conjuntos de IPs, rangos o puertos de manera más eficiente. Sin embargo, su uso añade una capa de complejidad adicional al proceso de configuración y gestión.

  3. Extensiones con sintaxis inconsistente:
    iptables cuenta con extensiones como xtables, que amplían sus funcionalidades al incluir capacidades avanzadas como el seguimiento de conexiones o la inspección profunda de protocolos. No obstante, la sintaxis de estas extensiones no siempre es uniforme ni intuitiva, lo que puede dificultar su uso y generar confusiones en configuraciones complejas.

En cambio, desde su incorporación, nftables ha demostrado ser una herramienta eficiente y moderna para la gestión del tráfico de red en Linux. Una de sus principales innovaciones es consolidar las herramientas clásicas como iptables, ip6tables, ebtables y arptables en una única interfaz, simplificando tanto la configuración como el mantenimiento.

Conceptos claves de nftables

Contexto y Familias de Direcciones

En iptables, cada tipo de tráfico o protocolo tenía su propio comando o herramienta específica, que funcionaba de manera independiente:

Comando/herramienta Propósito
iptables Filtrar y manipular tráfico IPv4.
ip6tables Filtrar y manipular tráfico IPv6.
arptables Filtrar y manipular tráfico ARP.
ebtables Filtrar tráfico en bridges (Ethernet frames).

Cada una de estas herramientas tenía su propia sintaxis y requería comandos separados para manejar diferentes tipos de tráfico, lo que podía ser confuso y propenso a errores.

Problemas del enfoque de iptables
  1. Fragmentación:
    • Si querías manejar tráfico IPv4 e IPv6, debías configurar reglas separadas con iptables e ip6tables, incluso si las reglas eran idénticas.
    • Lo mismo ocurría si querías gestionar tráfico ARP con arptables o tráfico en bridges con ebtables.
  2. Falta de unificación:
    • Cada herramienta tenía su propia lógica, lo que aumentaba la complejidad administrativa.
    • No existía una forma centralizada de gestionar tráfico que abarcase múltiples protocolos o tipos de datos.
  3. Mantenimiento difícil:
    • Las configuraciones de firewall requerían gestionar múltiples conjuntos de reglas, lo que hacía el mantenimiento y la depuración más complicados.
Cómo nftables mejoró este enfoque:
  1. Introducción de familias: En nftables, las familias permiten manejar diferentes tipos de tráfico dentro de un mismo marco conceptual, con una sintaxis unificada.

    • Familia ip: Maneja tráfico IPv4.
    • Familia ip6: Maneja tráfico IPv6.
    • Familia inet: Unifica IPv4 e IPv6.
    • Familia arp: Maneja tráfico ARP.
    • Familia bridge: Maneja tráfico L2 en bridges.
    • Familia netdev: Maneja tráfico en interfaces específicas.
  2. Unificación de reglas:

    • Con la familia inet, puedes escribir reglas que funcionen tanto para IPv4 como para IPv6.
    • Esto simplifica significativamente la configuración y el mantenimiento del firewall.
  3. Menor duplicación de configuraciones: En lugar de mantener múltiples herramientas (iptables, ip6tables, arptables, ebtables), todo se gestiona con el comando nft.

Ejemplos en nftables:

# Una regla para IPv4 e IPv6
nft add rule inet filter input ip saddr 192.168.0.0/24 accept
nft add rule inet filter input ip6 saddr fc00::/7 accept
nft add table arp filter
nft add chain arp filter input { type filter hook input priority 0; }
# Permitir solicitudes ARP desde el rango 192.168.1.0/24 en eth0
nft add rule arp filter input iif "eth0" arp sip 192.168.1.0/24 arp op request accept

# Bloquear cualquier otra solicitud ARP
nft add rule arp filter input iif "eth0" arp op request drop
nft add table bridge filter
nft add chain bridge filter forward { type filter hook forward priority 0; }
# Permitir solicitudes ARP desde direcciones MAC específicas en el bridge br0
nft add rule bridge filter forward iif "br0" ether type arp arp op request ether saddr 00:11:22:33:44:55 accept
nft add rule bridge filter forward iif "br0" ether type arp arp op request ether saddr 00:aa:bb:cc:dd:ee accept

# Bloquear cualquier otra solicitud ARP en el bridge
nft add rule bridge filter forward iif "br0" ether type arp arp op request drop
Diferencia entre las familias inet y netdev

La familia netdev puede ser una opción adecuada para implementar policing con nftables, dependiendo del escenario. Esto se debe a que la familia netdev opera en el hook ingress, lo que significa que puede evaluar el tráfico directamente en la interfaz antes de que llegue al stack de red.

Vamos a explorar por qué usar la familia netdev podría ser más apropiado para policing en algunos casos y cuándo es mejor utilizar otras familias.

Por qué usar la familia netdev para policing:
  1. Posición temprana en el flujo del tráfico:
  2. La familia netdev opera en el hook ingress, lo que significa que intercepta el tráfico inmediatamente después de que llega a la interfaz de red y antes de que sea procesado por el stack TCP/IP.
  3. Esto permite implementar políticas de policing directamente en el nivel de la interfaz, evitando sobrecargar el sistema con tráfico innecesario.

Ejemplo de regla de policing en netdev:

nft add table netdev ingress
nft add chain netdev ingress mychain { type filter hook ingress priority 0; }
nft add rule netdev ingress mychain ip limit rate 10 mbps drop
  • Esta regla limita el tráfico IP entrante a 10 Mbps en la interfaz.

  • Evitar procesamiento innecesario:

  • Como netdev actúa antes de que los paquetes sean entregados al stack TCP/IP, los paquetes que no cumplen con las políticas son descartados sin consumir recursos adicionales del sistema.

  • Ideal para interfaces específicas:

  • Cuando el objetivo es aplicar policing solo en una interfaz específica (por ejemplo, eth0), la familia netdev es la opción más directa.

  • Compatibilidad con limit rate:

  • La familia netdev admite acciones como limit rate, que es esencial para configurar políticas de policing basadas en rates.
¿Cuándo usar netdev frente a otras familias (ip, inet)?
Criterio Familia netdev Familias ip/inet
Posición en el stack Hook ingress, antes del stack TCP/IP. Hook prerouting, forward, o input, después de ingresar al stack.
Eficiencia Muy alta: paquetes descartados antes de procesarse. Menor: los paquetes son procesados parcialmente antes de aplicar las políticas.
Tráfico inspeccionado Solo tráfico entrante en una interfaz específica. Tráfico en todas las interfaces (dependiendo de la configuración).
Complejidad de las reglas Más limitada: ideal para políticas simples (policing básico). Más flexible: soporta reglas complejas (filtrado avanzado, conntrack).
Uso típico Policing directo en una interfaz (DDoS, límites de ancho de banda). Filtrado y políticas basadas en estado o dirección IP.

Limitaciones de la familia netdev:
  1. Solo para tráfico entrante:
  2. La familia netdev opera únicamente en el hook ingress, por lo que no puedes aplicar policing en el tráfico saliente de una interfaz.

  3. Falta de seguimiento de conexiones (conntrack):

  4. No puedes usar estados como NEW o ESTABLISHED, ya que netdev opera antes de que el módulo conntrack pueda procesar el tráfico.

  5. Menor flexibilidad:

  6. Si necesitás reglas más complejas basadas en capas superiores (por ejemplo, L7 o estados de conexión), las familias ip o inet son más adecuadas.
¿Cuándo es mejor usar la familia netdev para policing?
  • Tráfico entrante en una interfaz específica: Cuando necesitás limitar o descartar tráfico directamente en una interfaz antes de que afecte el sistema. Ejemplo: Mitigar un ataque DDoS en eth0.

  • Simplicidad y eficiencia: Si solo necesitas evaluar tráfico basándote en tasas o direcciones básicas (IP, MAC).

Ejemplo práctico:

Supongamos que queremos limitar el tráfico entrante a 100 Mbps en una interfaz específica (eth0):

nft add table netdev ingress
nft add chain netdev ingress eth0_chain { type filter hook ingress priority 0; }
nft add rule netdev ingress eth0_chain limit rate 100 mbps drop

Diferencia entre la familia bridge y netdev

Dado que la tanto la tabla bridge como la tabla netdev operan en Capa 2 uno podría preguntarse, qué diferencia hay entre estas dos familias, aquí tenemos una tabla comparativa detallada entre las familias bridge y netdev en nftables:

Aspecto Familia bridge Familia netdev
Nivel del modelo OSI Capa 2 (L2) Capa 2 (L2), pero en el contexto de interfaces individuales.
Tráfico manejado Tráfico que cruza un bridge (tramas Ethernet). Todo el tráfico en una interfaz específica antes de llegar al stack de red.
Propósito principal Controlar y filtrar tráfico dentro de bridges. Filtrar tráfico en una interfaz individual (sin importar si forma parte de un bridge).
Ámbito de aplicación Solo funciona en interfaces que forman parte de un bridge. Funciona en cualquier interfaz (física o virtual), bridge o no.
Casos de uso típicos - Aislar tráfico entre puertos de un bridge.
- Proteger bridges contra broadcast storms, spoofing de MAC, etc.
- Controlar tráfico entre VLANs en un bridge.
- Filtrar tráfico directo en una interfaz (ingress/egress).
- Detectar y mitigar DDoS a nivel de interfaz.
- Filtrar ARP o IPv4 antes de pasar al stack de red.
Compatibilidad con VLANs Sí, puede filtrar tráfico basado en etiquetas VLAN (802.1Q). No trabaja específicamente con VLANs.
Estado de conexión No tiene integración con conntrack. No tiene integración con conntrack.
Posición en el stack Filtra tráfico que pasa a través de un bridge antes de ser reenviado. Filtra tráfico directamente en una interfaz, antes de ser procesado por el stack de red.
Soporte de ARP Sí, puede filtrar tramas ARP. Sí, puede filtrar tramas ARP.
Soporte de IPv4/IPv6 Sí, puede inspeccionar tráfico IP encapsulado. Sí, puede inspeccionar tráfico IP encapsulado.
Direcciones MAC Puede filtrar según direcciones MAC de origen y destino. Puede filtrar según direcciones MAC de origen y destino.
Uso con interfaces individuales No, requiere que las interfaces formen parte de un bridge. Sí, funciona en cualquier interfaz individual.
Reemplaza a... ebtables. tc ingress para ciertas tareas de filtrado.
Limitaciones - Solo opera en tráfico dentro de bridges.
- No puede controlar tráfico de interfaces individuales fuera del bridge.
- No tiene integración con conntrack.
- No puede manejar tráfico reenviado entre interfaces.

Diferencias clave entre bridge y netdev:
  1. Ámbito del tráfico manejado:
  2. bridge: Filtra tráfico que cruza un bridge, conectando múltiples interfaces dentro del mismo dominio de difusión.
  3. netdev: Filtra tráfico directo en una interfaz individual, antes de pasar al stack de red.

  4. Dependencia de bridges:

  5. bridge: Solo funciona en interfaces que son parte de un bridge.
  6. netdev: Funciona en cualquier interfaz, sin importar si es parte de un bridge o no.

  7. Posición en el stack de red:

  8. bridge: Opera después de que el tráfico ha ingresado al bridge pero antes de ser reenviado a otra interfaz.
  9. netdev: Opera antes de que el tráfico entre al stack de red del sistema operativo, en el hook ingress.

  10. Casos de uso:

  11. bridge: Aislar tráfico dentro del bridge, controlar VLANs, proteger contra ataques L2 como spoofing de MAC.
  12. netdev: Inspeccionar todo el tráfico que entra o sale de una interfaz específica, ideal para mitigación temprana de DDoS o filtros basados en MAC/IP/ARP.

¿Cuándo usar cada una?
Usa la familia bridge si:
  • Tenemos configurado un bridge que conecta múltiples interfaces.
  • Necesitamos controlar tráfico entre dispositivos conectados al mismo bridge.
  • Queremos aislar tráfico entre VLANs o puertos del bridge.
  • Deseamos implementar políticas de seguridad para tráfico L2 dentro del bridge.
Usa la familia netdev si:
  • Queremos filtrar tráfico directo en una interfaz individual, incluso si no es parte de un bridge.
  • Necesitamos mitigar DDoS o ataques en el hook más temprano (ingress), antes de que el tráfico llegue al stack de red.
  • Deseamos inspeccionar tráfico ARP, IPv4 o IPv6 en una interfaz específica.

Ejemplos prácticos:

Con bridge:
  1. Bloquear tráfico entre dos interfaces en un bridge:
nft add table bridge filter
nft add chain bridge filter forward { type filter hook forward priority 0; }
nft add rule bridge filter forward iif "eth0" oif "eth1" drop
nft add rule bridge filter forward iif "eth1" oif "eth0" drop
  1. Permitir solo tráfico ARP en el bridge:
nft add rule bridge filter forward ether type arp accept
nft add rule bridge filter forward drop
Con netdev:
  1. Bloquear todo el tráfico IPv4 en una interfaz específica (eth0):
nft add table netdev filter
nft add chain netdev filter ingress { type filter hook ingress priority 0; }
nft add rule netdev filter ingress iif "eth0" ip drop
  1. Mitigar ataques DDoS limitando paquetes por segundo:
nft add rule netdev filter ingress iif "eth0" ip limit rate 1000/second drop
Entonces:
  • Familia bridge: Para manejar tráfico dentro de un bridge, enfocándose en L2 (direcciones MAC, VLANs, tramas Ethernet).
  • Familia netdev: Para filtrar tráfico temprano en una interfaz específica, independientemente de si forma parte de un bridge.

Ambas familias trabajan en L2, pero con propósitos distintos y aplicables en diferentes escenarios.

Resumen:

En iptables, no existía el concepto de familias. En su lugar, se usaban herramientas separadas para cada protocolo o tipo de tráfico (iptables, ip6tables, arptables, ebtables). Esto hacía que la configuración fuera fragmentada y menos eficiente.

Con la llegada de nftables, se introdujo el concepto de familias para unificar la gestión de diferentes tipos de tráfico, mejorando la flexibilidad, la simplicidad y el mantenimiento de las reglas de firewall. Este cambio fue una de las razones clave por las que nftables es considerado un reemplazo moderno y más avanzado.

Conocer en profundidad nftables y el concepto de las familias, tiene implicancias sumamente prácticas. Por ejemplo: ¿qué familia usarías para poder separar en una misma LAN el tráfico inalámbrico del cableado? Lo dejo para que lo pienses. 😊

Fuentes y más información

¿SELinux? Nah, dejalo en Disabled

En el mundo real muchos sysadmins y developers en sistemas operativos de la familia Red Hat deshabilitan SELinux. "¡Es muy difícil!" dicen. En Ubuntu ni en Debian existe esa cuestión ya que en su lugar se usa AppArmor. Este post no te dará servido en bandeja como solucionar un problema, sino algo mucho más valioso, que es entender el fundamento del comportamiento de SELinux y por qué funciona la solución que vayas a aplicar.

Usamos un caso práctico: tenemos una instalación basada en Laravel, php-fpm y con nginx como web server.

Tenemos una estructura de directorio así:

> tree -L 1 /home/sergio/laravel-voyager/
/home/sergio/laravel-voyager/
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── lang
├── package.json
├── phpunit.xml
├── public
├── README.md
├── resources
├── routes
├── storage
├── tests
├── vendor
└── vite.config.js

11 directories, 7 files

Veamos la información de SELinux para algunos archivos y directorios:

[sergio@masterk laravel-voyager]$ sudo ls -Zd public/storage /etc/nginx /home/sergio/.bashrc /root/.bashrc /usr/share/nginx/ bootstrap/cache/
[sudo] password for sergio: 
unconfined_u:object_r:user_home_t:s0 bootstrap/cache/      unconfined_u:object_r:user_home_t:s0 public/storage
 system_u:object_r:httpd_config_t:s0 /etc/nginx               system_u:object_r:admin_home_t:s0 /root/.bashrc
    system_u:object_r:user_home_t:s0 /home/sergio/.bashrc            system_u:object_r:usr_t:s0 /usr/share/nginx/

Si bien la salida es demasiado críptica, la podemos descifrar:

Archivo/Directorio Usuario (SELinux) Rol (SELinux) Asignación basada en tipos (SELinux) Rango (SELinux) Descripción y Observaciones
bootstrap/cache/ unconfined_u object_r user_home_t s0 Considerado como parte del directorio personal de un usuario. Debe ser httpd_sys_rw_content_t para Laravel.
public/storage unconfined_u object_r user_home_t s0 Similar a bootstrap/cache. Debe ser httpd_sys_content_t para que Laravel sirva archivos estáticos.
/etc/nginx system_u object_r httpd_config_t s0 Archivo de configuración de Nginx. Correctamente etiquetado como httpd_config_t. No requiere cambios.
/home/sergio/.bashrc system_u object_r user_home_t s0 Archivo personal de configuración de un usuario. Etiquetado como user_home_t, lo cual es correcto.
/root/.bashrc system_u object_r admin_home_t s0 Archivo de configuración del usuario root. Etiquetado como admin_home_t, lo cual es correcto.
/usr/share/nginx/ system_u object_r usr_t s0 Etiquetado como usr_t, que es genérico. Debe ser httpd_sys_content_t si Nginx sirve contenido estático aquí.

Descripción de los campos

Concepto Descripción Ejemplo
Usuario (SELinux) Define el usuario de SELinux asociado al recurso. system_u: Archivo gestionado por el sistema.
unconfined_u: Archivo de un usuario sin restricciones.
Rol (SELinux) Define el rol asignado al recurso. Usualmente es object_r para archivos regulares y directorios.
Asignación basada en tipos Determina qué procesos o aplicaciones pueden interactuar con un recurso. httpd_sys_rw_content_t: Permite lectura/escritura por parte del servidor web.
httpd_sys_content_t: Permite solo lectura por parte del servidor web.
user_home_t: Para directorios o archivos en /home.
Rango (SELinux) Se usa para configuraciones de seguridad MLS o TE (usualmente s0 en configuraciones estándar). Normalmente no requiere ajustes en configuraciones básicas.

Ahora bien, hay varios tipos de políticas en cuanto a funcionalidades que se pueden aplicar en SELinux, la que se usa en general es targeted. El tipo de política targeted significa que determinados servicios se ejecutan de manera confinada, es decir limitada para realizar solamente determinadas acciones más allá de los permisos ordinarios.

Y es importante enfatizar que cuando usamos targeted el campo que más nos tiene que interesar de un contexto es el "Type Enforcement". En general no prestaremos demasiada atención al resto de los campos.

Lo más importante para entender es que las reglas de SELinux tienen sujetos y objetos. El contexto o etiqueta del objeto podría no permitir que el sujeto realice una determinada acción al sujeto, y es allí cuando se produce un error. Dicho error tiene por objetivo proteger al sistema. En este ejemplo los sujetos son nginx y php-fpm y los objetos son los directorios bootstrap/cache y public/storage (y sus respectivos contenidos).

Por ejemplo, nginx sería el sujeto y con el siguiente comando vemos el contexto del proceso:

> ps -C nginx,php-fpm -Z
LABEL                               PID TTY          TIME CMD
system_u:system_r:httpd_t:s0        898 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1118 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1119 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1120 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1121 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1122 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1451 ?        00:00:00 nginx
system_u:system_r:httpd_t:s0       1452 ?        00:00:00 nginx
system_u:system_r:httpd_t:s0       1453 ?        00:00:00 nginx

Pero habrá problemas...

La cuestión es que tanto nginx como php-fpm se ejecutan con la asignación de tipo (TE) httpd_t y la TE que tienen directorior como bootstrap/cache y public/storage es user_home_t.

Con el siguiente comando podemos verifucar qué cosas puede hacer el dominio httpd_t en el contexto del objeto que tiene el tipo de asignación user_home_t:

> sesearch -s httpd_t -A -t user_home_t
allow daemon user_home_t:file { append getattr };
allow domain file_type:blk_file map; [ domain_can_mmap_files ]:True
allow domain file_type:chr_file map; [ domain_can_mmap_files ]:True
allow domain file_type:file map; [ domain_can_mmap_files ]:True
allow domain file_type:lnk_file map; [ domain_can_mmap_files ]:True
allow httpd_t file_type:dir { getattr open search };
allow httpd_t file_type:filesystem getattr;
allow httpd_t user_home_t:file map; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr ioctl lock open read search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_enable_homedirs ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:lnk_file { getattr read }; [ httpd_enable_homedirs ]:True

Cada regla tiene:

  • El tipo de regla ( allow )
  • El sujeto de la regla ( httpd_t )
  • El contexto y tipo del objeto ( user_home_t:file )
  • Los permisos (append y getattr)

¡No hay ninguna regla que permita la modificación total del contenido del archivo ni la creación de archivos en la carpeta (por ejemplo, el permiso write)!. Esto significa que, de manera predeterminada, servicios como apache o nginx no tienen permiso para escribir en los directorios home de los usuarios. Y por ese motivo hay cosas que fallarán, sin importar los permisos ni el usuario...

Por lo tanto los directorios public/storage y bootstrap/cache y su contenido necesitan otro contexto para que Laravel funcione correctamente cuando está ubicado en un subdirectorio de /home. Mirando la documentación podemos ver que httpd_sys_content_t y httpd_sys_rw_content son respectivamente los apropiados para esos directorios y su contenido:

httpd_sys_content_t

- Set files with the httpd_sys_content_t type, if you want to treat the files as httpd sys content.

Paths:
     /srv/([^/]*/)?www(/.*)?,  /var/www(/.*)?,  /etc/htdig(/.*)?,   /srv/gallery2(/.*)?,   /var/lib/trac(/.*)?,   /var/lib/htdig(/.*)?,
     /var/www/icons(/.*)?,     /usr/share/glpi(/.*)?,     /usr/share/htdig(/.*)?,     /usr/share/drupal.*,     /usr/share/z-push(/.*)?,
     /var/www/svn/conf(/.*)?,         /usr/share/icecast(/.*)?,          /var/lib/cacti/rra(/.*)?,          /usr/share/ntop/html(/.*)?,
     /usr/share/nginx/html(/.*)?,      /usr/share/doc/ghc/html(/.*)?,      /usr/share/openca/htdocs(/.*)?,      /usr/share/selinux-pol
     icy[^/]*/html(/.*)?

y

httpd_sys_rw_content_t

- Set files with the httpd_sys_rw_content_t type, if you want to treat the files as httpd sys read/write content.

Paths:
     /etc/rt(/.*)?,  /etc/glpi(/.*)?,  /etc/horde(/.*)?,  /etc/drupal.*,  /etc/z-push(/.*)?,  /var/lib/svn(/.*)?,   /var/www/svn(/.*)?,
     /etc/owncloud(/.*)?,    /var/www/html(/.*)?/uploads(/.*)?,    /var/www/html(/.*)?/wp-content(/.*)?,   /var/www/html(/.*)?/wp_back
     ups(/.*)?,  /var/www/html(/.*)?/sites/default/files(/.*)?,  /var/www/html(/.*)?/sites/default/settings.php,  /etc/mock/koji(/.*)?,
     /etc/nextcloud(/.*)?,       /var/lib/drupal.*,      /etc/zabbix/web(/.*)?,      /var/lib/moodle(/.*)?,      /var/log/z-push(/.*)?,
     /var/spool/gosa(/.*)?,   /etc/WebCalendar(/.*)?,   /usr/share/joomla(/.*)?,   /var/lib/dokuwiki(/.*)?,    /var/lib/owncloud(/.*)?,
     /var/spool/viewvc(/.*)?, /var/lib/nextcloud(/.*)?, /var/lib/pootle/po(/.*)?, /var/lib/phpMyAdmin(/.*)?, /var/www/moodledata(/.*)?,
     /srv/gallery2/smarty(/.*)?,      /var/www/moodle/data(/.*)?,      /var/lib/graphite-web(/.*)?,      /var/log/shibboleth-www(/.*)?,
     /var/www/gallery/albums(/.*)?,  /var/www/html/owncloud/data(/.*)?, /var/www/html/nextcloud/data(/.*)?, /usr/share/wordpress-mu/wp-
     content(/.*)?, /usr/share/wordpress/wp-content/upgrade(/.*)?, /usr/share/wordpress/wp-content/uploads(/.*)?, /var/www/html/config
     uration.php

De modo que esos son los contextos que tendrías que aplicar a esos directorios y sus contenidos. El resto, si sos administrador o desarrollador de Linux con algo de experiencia es anecdótico.

Fuentes y más recursos

Cómo Hacer Túneles SSH

Una de las funcionalidades extraordinarias que posee openssh es la de encapsular el tráfico inseguro a puertos TCP. A continuación vemos algunos ejemplos útiles de cuándo y cómo utilizarlos.

Túneles

unsplash-logo Aaron Burden

Algunas suposiciones:

  • openssh-server.example.com: Es el host que posee el servicio ssh y el servicio sin cifrar
  • otrohost.example.com: Es un host que posee un servidor web sin TLS pero que carece de ssh.
  • 192.0.2.1/24: Es la dirección IP del cliente ssh.
  • filtrado.example.com: Es un host que tiene el puerto del servicio al que queremos acceder pero se encuentra filtrado para el exterior.
  • casa.example.com: Es un host remoto respecto a un servidor ssh accesible desde la red de filtrado.example.com. Este host está en un lugar en el cual podemos trabajar cómodamente 😊 por fuera de la infraestructura de filtrado.example.com.

Caso 1: Redireccionado desde un puerto local a un puerto remoto para segurizar un servicio sin cifrar

Queremos segurizar el acceso a un servidor web sin TLS.

ssh -N -f -L 10001:localhost:80 openssh-server.example.com

  • 80: Es el puerto que tiene el servicio sin cifrar
  • localhost: Es la dirección en el host servicio-remoto.example.com que escucha en el puerto 80.
  • 10001: Es el puerto local al cual tendremos que conectarnos para obtener el servicio web.
  • -N No ejecuta comandos en el lado remoto.
  • -f El proceso va al segundo plazo antes de la ejecución de comandos.

El url que habrá que poner en el navegador es http://localhost:10001

Caso 2: Redireccionado desde un puerto local a un puerto remoto permitiendo el acceso al túnel usuarios de la misma red local para Segurizar un servicio sin cifrar

Queremos permitir que otros hosts de la red puedan acceder nuestro servicio encapsulado.

ssh -g -N -f -L 10001:localhost:80 openssh-server.example.com

  • -g Permite acceder a otros hosts de la red 192.0.2.0/24 acceder a el url http://192.0.2.1:10001/

Caso 3: Redireccionado desde un puerto local a un puerto remoto usando otro servidor ssh para acceder a un servicio sin TLS ni ssh

ssh -N -f -L 10001:otrohost.example.com:80 openssh-server.example.com

  • Aquí la url será http://localhost:10001

Caso 4: Crear un túnel en dirección inversa para acceder a un puerto SSH filtrado (utilización de un túnel remoto)

En este caso, queremos acceder al puerto SSH de filtrado.example.com, que no es accesible desde internet. Utilizamos casa.example.com con acceso SSH temporal para redirigir el puerto SSH de filtrado.example.com.

El comando debe ejecutarse en filtrado.example.com:

ssh  -N -f -R 9000:localhost:22 casa.example.com
  • 22: Es el puerto SSH de filtrado.example.com.
  • 9000: Es el puerto redirigido en casa.example.com.

Para conectarse al puerto SSH de filtrado.example.com desde casa.example.com:

ssh -p 9000 localhost

Caso 5: Redireccionamiento remoto para permitir acceso compartido a un puerto filtrado

En este caso, queremos redirigir el puerto SSH de un servidor filtrado (filtrado.example.com) hacia un servidor intermedio (casa.example.com) y permitir que otros hosts que están en la misma red local puedan acceder al túnel.

Comando para configurar el túnel:

ssh  -N -f -R 0.0.0.0:9000:localhost:22 casa.example.com
  • 0.0.0.0:9000: Permite que el puerto redirigido esté accesible en todas las interfaces de red de casa.example.com.
  • localhost:22: Redirige el tráfico al puerto SSH de filtrado.example.com.

Para conectarse al túnel desde cualquier host con acceso a casa.example.com:

ssh -p 9000 casa.example.com
Requisito en casa.example.com

Habilitar GatewayPorts en /etc/ssh/sshd_config:

GatewayPorts clientspecified

Con esta configuración si el cliente no especifica explícitamente 0.0.0.0 al crear el túnel, los puertos siguen siendo locales por defecto.

Reinicia el servicio SSH:

sudo systemctl restart sshd
Notas de Seguridad

Si el servidor casa.example.com tiene una IP pública, usar firewalls para limitar el acceso al puerto 9000 y restringirlo solo a las máquinas necesarias. Para mayor seguridad, se puede especificar una interfaz específica en lugar de 0.0.0.0.

Troubleshooting

En todas las casos usar la opción -v para obtener realizar debugging. Recordar que dicha opcioń se puede usar múltiples veces para aumentar el detalle.

Como instalar Zabbix 7.x usando contenedores podman

Namespaces en Linux

Zabbix es una potente herramienta de monitoreo. Recientemente salió la versión 7.0 la cual incorporó funcionalidades importantes, como son los items de browser que usan webdrivers. A continuación veremos como implementar Zabbix 7.x usando podman.

La guía siguiente supone que:

  • Ya generaste o adquiriste los certificados ssl.
  • Tenés los conocimientos fundamentales sobre Linux (usamos RHEL8).
  • Poseés conocimientos básicos de troubleshooting en contenedores y entendiendo que este "HowTo" es un punto al que deberás adaptar a tu entorno y necesidades. No es consultoría 😀.

1. Crear el Pod con Puertos Correctos

Primero, crear el pod con el puerto 443 en el host mapeado al puerto 8443 en el contenedor de NGINX. También asignar el puerto 10051 para Zabbix Server.

podman pod create --name zabbix -p 443:8443 -p 10051:10051

2. Desplegar el Contenedor MySQL

Iniciar el contenedor mysql-server dentro del pod, configurando las variables de entorno necesarias para la base de datos de Zabbix:

mkdir mysql && podman run -d --name mysql-server --pod=zabbix \
    -v ./mysql:/var/lib/mysql:Z \
    -e MYSQL_DATABASE="zabbix" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    -e MYSQL_ROOT_PASSWORD="root_pwd" \
    docker.io/library/mysql:8.0

3. Configurar log_bin_trust_function_creators en MySQL

Para evitar problemas con permisos de creación de funciones, habilita esta configuración en el servidor MySQL:

Darle tiempo a que MySQL esté listo:

until podman exec mysql-server mysqladmin ping -u root -p'root_pwd' --silent; do
    echo "Esperando a que MySQL esté listo..."
    sleep 5
done

Ejecutar la configuración:

podman exec -i mysql-server mysql -u root -p'root_pwd' -e "SET GLOBAL log_bin_trust_function_creators = 1;"

4. Desplegar el Contenedor Zabbix Server

Iniciar el contenedor zabbix-server-mysql en el pod, conectándolo a MySQL y asegurándote de que se conecte al Java Gateway:

podman run -d --name zabbix-server-mysql --pod=zabbix \
    -e DB_SERVER_HOST="127.0.0.1" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    -e MYSQL_DATABASE="zabbix" \
    -e ZBX_JAVAGATEWAY="127.0.0.1" \
    docker.io/zabbix/zabbix-server-mysql:alpine-latest

5. Verificar la Creación de Tablas en la Base de Datos

Confirmar que zabbix-server-mysql crea correctamente las tablas en la base de datos:

podman exec -i mysql-server mysql -u root -p'root_pwd' -e "USE zabbix; SHOW TABLES;"

6. Desplegar el Contenedor Zabbix Java Gateway

Iniciar el contenedor zabbix-java-gateway, asegurándote de que esté en el pod zabbix:

podman run -d --name zabbix-java-gateway --pod=zabbix docker.io/zabbix/zabbix-java-gateway:alpine-latest

7. Desplegar el Contenedor NGINX con Certificados SSL y Configuración

Ejecutar el contenedor de Zabbix Web NGINX MySQL (zabbix-web-mysql-ssl), montando los certificados y el archivo de configuración zabbix-ssl.conf correctamente:

El archivo de configuración zabbix-web-mysql-ssl puede ser algo así:

server {
    listen 8443 ssl;
    server_name _;

    ssl_certificate /etc/ssl/nginx/fullchain.pem;
    ssl_certificate_key /etc/ssl/nginx/privkey.pem;

    root /usr/share/zabbix;

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/tmp/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /usr/share/zabbix$fastcgi_script_name;
    }

    error_log /tmp/zabbix_ssl_error.log;
    access_log /tmp/zabbix_ssl_access.log;
}
podman run -d --name zabbix-web-mysql-ssl --pod=zabbix \
    -v ./certs/fullchain.pem:/etc/ssl/nginx/fullchain.pem:ro \
    -v ./certs/privkey.pem:/etc/ssl/nginx/privkey.pem:ro \
    -v ./zabbix-ssl.conf:/etc/nginx/http.d/zabbix-ssl.conf:ro \
    -e ZBX_SERVER_HOST="127.0.0.1" \
    -e DB_SERVER_HOST="127.0.0.1" \
    -e MYSQL_DATABASE="zabbix" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    docker.io/zabbix/zabbix-web-nginx-mysql:alpine-latest

8. Verificación Final

Hacer una solicitud a la interfaz de Zabbix para confirmar que todo esté funcionando y que la configuración SSL esté activa:

curl -v  https://sebelk.lab

Deberías ver una respuesta HTTP 200 desde NGINX, indicando que Zabbix está activo y funcionando con SSL.

9. Instalación del agente

podman run -d --name zabbix-agent --pod=zabbix \
    -e ZBX_SERVER_HOST="127.0.0.1" \
    -e ZBX_SERVER_PORT="10051" \
    -e ZBX_HOSTNAME="ZabbixServerAgent" \
    docker.io/zabbix/zabbix-agent2:alpine-latest

10. Deshabilitar variable en MySQL

La variable que habilitamos antes no es imprescindible, de modo que se puede desactivar:

podman exec -i mysql-server mysql -u\
     root -p'root_pwd'\
    -e "SET GLOBAL log_bin_trust_function_creators = 0;"

Todo listo

¡Listo! Trabajo terminado 😉

Zabbix Implementado usando podman

¿Qué te pareció la guía? Podés usar la sección de Comentarios.