ufw-mcp
ufw-mcp: permiso UFW de mínimos privilegios para los servidores VoIP
Propuesta. Nada de esto se ha aplicado a ningún servidor.
Host previsto: ufw-mcp.example.com (coincide con itglue-mcp, proxmox-mcp,
network-mcp). Servidor separado de network-mcp, porque la garantía de solo-lectura
de network-mcp está impuesta por AST y añadir una ruta de escritura elimina la
propiedad que lo hace seguro para entregarlo a cinco ingenieros. La misma separación
que tactical-rmm-mcp / tactical-rmm-audit-mcp.
El comando interno que automatiza
Según Dan, y confirmado en el historial del shell, dos reglas por dirección:
sudo ufw allow from <ip> to any port 5060 proto tcp
sudo ufw allow from <ip> to any port 5060 proto udpHecho en bloque a través de los rangos de un operador, y luego verificado con
ufw status | grep 5060 | grep -c tcp / -c udp. La herramienta coincide con esa forma:
el wrapper gestiona UNA dirección (para que el código privilegiado siga siendo pequeño), la
herramienta MCP itera para el bloque, y devuelve los conteos antes/después de la misma manera.
Related MCP server: toolfence
Lo que la herramienta añade frente a escribir el comando
Se niega cuando el allow fallaría silenciosamente.
UFW funciona por primera coincidencia y ufw allow AÑADE AL FINAL. En estos servidores
hay ~3.600 reglas con ~2.150 entradas DENY ocupando la parte superior. Así que si la
dirección ya está denegada, el allow cae por debajo, el comando tiene éxito, la regla
aparece en ufw status, y ningún paquete pasa nunca.
Esto no es hipotético. Encontrado en vivo el 2026-08-17, en solo lectura:
core1 [2145] DENY IN 198.51.100.36 # FraudWatch 2026-07-15 example-isp-NY /32
[3371] 5060/tcp ALLOW IN 198.51.100.36 <- dead
[3372] 5060/udp ALLOW IN 198.51.100.36 <- dead
core2 [2145] DENY IN 198.51.100.36 # FraudWatch 2026-07-15 example-isp-NY /32
[2150] DENY IN 198.51.100.42
[2914] 5060/tcp ALLOW IN 198.51.100.42 <- dead
[2915] 5060/udp ALLOW IN 198.51.100.42 <- dead
[3440] 5060/tcp ALLOW IN 198.51.100.36 <- dead198.51.100.42 también es inconsistente entre los dos núcleos: funcionando en
core1, muerta en core2. Ese cliente se presentaría como intermitente.
El wrapper detecta esto y sale con código 4 mostrando la línea problemática, en lugar de
informar éxito. Existe un argumento override-deny para insertar POR ENCIMA del deny,
pero deliberadamente no es el predeterminado: anular un bloqueo de fraude debería ser
una decisión, no un efecto secundario.
Auditoría: quién añadió o revocó qué
Tres capas, todas vinculadas a la identidad de Entra del JWT validado, nunca
a un argumento que proporcione el llamante. Esa es la propiedad que hace fiable
el rastro: está verificado por firma, no autodeclarado. network-mcp ya hace
esto en server.py::_actor() y el mismo código se traslada.
En la propia regla.
comment "mcp by:approver@example.com <ts>". La auditoría vive en el artefacto, así queufw statusnombra quién añadió cada regla para siempre, sin ningún log que correlacionar, visible para cualquiera en la máquina que nunca haya oído hablar del servidor MCP.En el servidor VoIP.
logger -t ufw-mcpescribe en el syslog de esa máquina, de modo que el registro existe donde ocurrió el cambio.En el servidor MCP. Un fichero de auditoría de solo-añadido con llamante, acción, destino, host y resultado. Debe vivir en un volumen con nombre o un redeploy de Portainer lo borra, lo cual es una trampa real aquí dado que el push-to-deploy reconstruye el contenedor.
Los registros de inicio de sesión de Entra son un cuarto registro independiente de quién se autenticó.
La revocación se registra de forma idéntica, y ufw-mcp-revoke solo elimina reglas
que llevan la etiqueta mcp by:, por lo que físicamente no puede borrar una entrada
de APIBAN o FraudWatch.
Mínimo privilegio en la máquina
La cuenta NO es un superusuario. Es un usuario ordinario con permiso para invocar una única acción privilegiada:
sudo useradd --create-home --shell /bin/bash --comment 'ufw allow automation' nocufwSin -G sudo, sin grupos suplementarios. Luego visudo -f /etc/sudoers.d/ufw-mcp:
Cmnd_Alias UFW_MCP = /usr/local/sbin/ufw-mcp-allow, \
/usr/local/sbin/ufw-mcp-revoke, \
/usr/local/sbin/ufw-mcp-list
nocufw ALL=(root) NOPASSWD: UFW_MCP
Defaults!UFW_MCP !requirettyNunca NOPASSWD: /usr/sbin/ufw *. ufw también tiene reset, disable y
delete, y los comodines de argumentos de sudoers son escapables de forma
rutinaria. El límite de seguridad es el WRAPPER; sudoers solo decide qué binario.
La trampa que convierte esto en una cuenta root: el wrapper debe ser propiedad
de root y no escribible por nocufw, ni estar en un directorio que nocufw pueda
escribir. Instalar con install -o root -g root -m 0755. Verificar toda la concesión
con sudo -l -U nocufw en ambos servidores.
Necesita un shell real, a diferencia de la cuenta relay, porque la herramienta MCP
ejecuta sudo ufw-mcp-allow ... por SSH y OpenSSH ejecuta los comandos remotos a
través del shell del usuario. El shell no es el privilegio; la línea de sudoers lo es.
apiban2ufw.sh: leído el 2026-08-17, y cambia una cosa
Se ejecuta cada 4 minutos desde /etc/cron.d/ns_apiban, como root.
No vacía ni reescribe nada. Considera solo reglas que llevan su propio comentario. Nuestras entradas no pueden ser destruidas.
Lo que importa: añade con
/usr/sbin/ufw insert 1 deny from $line to any comment "From APIBAN $dateVar"insert 1, no append. Así que APIBAN se coloca por encima de nosotros, cada cuatro
minutos. Eso significa que "insertar nuestro allow por encima del deny" no es una
posición duradera: está por encima de todo lo que existe en el momento en que lo
escribimos, y por debajo de cualquier cosa que APIBAN añada después.
El wrapper ahora lo dice explícitamente:
Deny de APIBAN -> insertar un allow por encima funciona hasta que APIBAN vuelva a listar la dirección, y entonces falla silenciosamente. La solución duradera está en el feed, no en UFW.
Deny manual / FraudWatch -> insertar por encima es duradero, porque nada lo vuelve a añadir.
Ambos ejemplo-isp-NY son FraudWatch, no APIBAN, así que esos están en el caso duradero.
Un problema latente que conocemos, no nuestro para arreglar hoy: remove_ips borra por
especificación de regla (ufw delete deny from <línea>), no por comentario. Si una
dirección aparece tanto en el feed APIBAN como en un deny manual, una eliminación del
feed podría borrar la regla manual.
Pendiente antes de construir
Decidir si eliminar un bloqueo por fraude está en alcance. Añadir un allow y borrar un DENY de FraudWatch son privilegios distintos. Recomiendo que la herramienta incluya solo allow y revoke-de-reglas-propias; eliminar un bloqueo por fraude debería ser una decisión, no un efecto secundario.
Revoke eliminado, 2026-08-17
El par ufw-mcp-revoke / revoke_ip se ha eliminado del diseño (commit 4f2e8c1), con
la misma justificación que el caso override-deny:
La herramienta MCP que llama a ese wrapper no puede autenticarse como nadie. Las reglas revocables se etiquetan con
comment "mcp by:<sub>". Cualquier persona que dispare el wrapper revoca el allow de cualquier otra persona que haya usado la herramienta. Un payload de prompt-injection o una auditoría de permiso perdida revoca las rutas de un colega. Un humano en el shell puede hacer eso, pero también puede hacerufw delete— no estamos construyendo una herramienta para hacer daño más fácil.Un privilegio más = un binario más con
NOPASSWDen la máquina. La superficie de ataque del archivo sudoers del host VoIP pasa de un binario con un único argumento permitido a dos binarios.El caso de uso real es un cambio de decisión, no una operación rutinaria. Cuando Intel471 lista una infraestructura de origen o se corrige un detalle de ruta, alguien tiene que mirar el ticket de fraude de todos modos y decidir, ya que el siguiente paso suele ser un re-allow con una justificación.
El retiro es limpio: el wrapper se elimina, la línea de sudoers se elimina, y allow
ahora hace la comprobación de "coincidencia exacta" contra los denies antes de continuar.
Respuesta a la auditoría anterior (network-mcp)
La caché de estado vive en un archivo con nombre canónico. El wrapper NUNCA usa
ufw status --raw. Cuando el archivo no existe, la herramienta falla con un error en lugar de intentar regenerarlo.cron.d/ufw-statusvuelve a escribirlo cada 5 minutos. La invocación por SSH apunta a la ruta absoluta y niega cualquier otra.Se ha eliminado el argumento
--jsondestatus; el wrapper lo traduce a un formato JSON estricto con tipos comprobados. La superficie expuesta al MCP no puede emitir texto arbitario de la máquina.Sin argumentos de puerto definidos por el llamador: el comentario
mcp by:queda fijo. El conjunto de cambios realizables es exactamente el que puede describir la regla del wrapper.sudoersno tiene!authenticate. Se conserva el prompt de contraseña; el sudoers que habilita el wrapper es indistinguible del que habilita cualquier otro comando de mantenimiento.
Lo que aprende el wrapper de la caché
El estado de UFW tarda un segundo en calcularse. El wrapper lee el snapshot en
'arranque en frío' para decidir cuándo aplicar insert en lugar de append:
Situación | Comportamiento |
El destino ya está |
|
El destino está |
|
El destino está |
|
Cualquier otra cosa |
|
El fallo en caso de deny hace que nuestra herramienta sea más segura que el comando manual,
y la salida es la línea exacta de ufw status para que quien esté de guardia pueda
verla sin depurar.
ufw status es lento
~3.600 reglas tardan ~45s en generarse. El wrapper corre tres veces por dirección si
se le llama de forma ingenua. El MCP la llama como mcp__ufw_allow:
Obtener el estado una vez.
Comprobar la regla, aplicar la sentencia.
Volver a obtener el estado solo si se mutó, y devolver el diff.
La lectura y la escritura no se bloquean entre sí porque solo usamos la cache, y si
ufw status falla, la herramienta falla con un mensaje, no con una suposición.
Implementación
Los wrappers de host, el par de herramientas MCP y las funciones compartidas viven en
el repositorio. Un ufw-mcp-*.sh de ~300 líneas en cada host VoIP, instalado con
install -o root -g root -m 0755, y un make install que también escribe
/etc/sudoers.d/ufw-mcp con el sudoers visto arriba. Un Playbook de Ansible aplica
eso en los dos hosts VoIP. La JWT de Entra se valida en el servidor MCP exactamente
igual que en los otros MCP; el servidor VoIP nunca ve el token.
El script se puede probar en un contenedor LXC de desecho antes de tocarlos.
También: reintento automático vs. repetir
Repetir MCP |
| |
Quién reintenta | El agente (a través del tool | El wrapper |
Bucle de reintento | Loop del agente: decisión, llamada, respuesta |
|
Qué cambia en cada intento | Nada; misma entrada |
|
Cuándo se detiene | Éxito, o el agente lo abandona | Éxito, o 30 intentos, o la llamada al sistema falla |
Observaciones | El agente es el punto único de decisión; el wrapper sigue siendo determinista | Menos viajes de ida y vuelta MCP, pero más complejidad en la parte privilegiada |
Para un "deny":
El agente ve un fallo
OUTDATED(el estado que tenía tenía más de 3 minutos).El agente VUELVE a llamar a
mcp__ufw__allow, esta vez conrefresh: true.El agente vuelve a evaluar la regla ya actualizada.
Para una "deny" que se convierte en "allow":
El agente ve el fallo
DENY_EXISTS.El agente deja que
retry.pyvuelva a intentar la regla durante un máximo de 15 minutos. La condición de éxito no es "UFW informa de un allow"; es que el servidor VoIP completa un registro SIP, que es lo que GXP1 necesita que ocurra.
Para una "deny" que sigue siendo denegada:
La llamada detrás de la deny no elige esperar 15 minutos.
El agente informa del fallo al ingeniero de guardia. Sin sorpresas.
Un servidor más con SSH abierto en el puerto 2222
El servidor UFW-MCP tiene un puerto SSH no estándar. De nuevo: modelo de compromiso, configuración de firewall, "principio de mínimo privilegio", insistencia en separar la automatización de MCP de la administración general.
Cómo comprobarlo en un portátil
Las herramientas UFW no se pueden ejecutar en macOS ni en Windows. ufw no existe.
La verificación se hace en el propio servidor VoIP: sudo -l -U nocufw debe listar
exactamente la línea del wrapper, install -o root -g root -m 0755 debe estar en
la nota de despliegue, y el conjunto sudo ufw deny proto + ufw allow debe
comportarse como se documenta aquí.
Referencias
El
loggeressudo -u recorder -p auth.info.La regla del proxy inverso es
# FIXME: hard-coding de credenciales(esto es un desliz de copiar/pegar, no una regla real — se corrige antes de cualquier despliegue).
El bucle de reintento no es ningún tipo de bucle
No hay problema. Hemos dejado una nota en el código y estamos en ello.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceGovernance engine for MCP tool calls, providing deterministic rule enforcement to block destructive actions like SQL drops, shell commands, and file system modifications before execution.2Apache 2.0
- AlicenseNot gradedqualityAmaintenanceA local, fail-closed firewall for MCP tool calls that enforces least-privilege policies and human approval between AI agents and stdio MCP servers.7MIT
- FlicenseNot gradedqualityCmaintenanceEnables approval-gated incident response workflows that gather evidence through read-only MCP tools, perform idempotent writes, and preserve a durable audit trail.1
- AlicenseNot gradedqualityCmaintenanceEnforces deterministic security policies as an inline firewall for MCP server tool calls, with AST-based validation, cryptographic audit logging, and CLI-based evaluation and verification.MIT
Related MCP Connectors
Remote MCP for AI Studio Android release gate MCP, structured receipts, audit logs, and reviewer-rea
Remote MCP for Copilot CLI switch gate MCP, structured receipts, audit logs, and reviewer-ready evid
Fail-closed action authorization, MCP risk scanning, x402 checks, and signed receipts.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/JohnGilligan2/ufw-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server