Lección 04
Hardening de servicios
Reduce capacidades, superficie y confianza entre componentes.
Antes de practicar
Hardening significa reducir posibilidades innecesarias antes de que un fallo las convierta en impacto. No consiste en activar muchas casillas ni en hacer que un servidor parezca complejo. Parte de una función conocida: qué debe leer, dónde debe escribir, qué red necesita, con qué identidad opera y qué acciones privilegiadas son indispensables. Después se niega lo demás por capas y se prueba el recorrido legítimo. Una restricción que nadie verifica puede romper la operación; una restricción que se desactiva ante el primer error nunca fue una defensa duradera. El resultado debe ser comprensible, repetible y observable.
Modelo de capacidad. Imagina que el proceso recibe una solicitud inesperada o que una dependencia falla. Los controles de endurecimiento limitan qué recursos podría alcanzar en ese escenario: archivos, identidades, llamadas al núcleo, dispositivos y destinos de red. No sustituyen corregir el defecto ni autenticar a quien usa el servicio; reducen el radio de daño. La pregunta central es «si este proceso hiciera algo distinto de su función, ¿qué le permitiría el sistema?». Responderla obliga a mirar permisos efectivos, no las intenciones del equipo ni el nombre de la tecnología.
Evitar autoridad total. Ejecutar como root para resolver una sola operación privilegiada mezcla esa necesidad con muchas capacidades no relacionadas. Primero pregunta si la operación puede desaparecer, realizarse antes del arranque o delegarse a un componente pequeño con una interfaz estrecha. Después evalúa permisos de archivos, grupos y capacidades del núcleo. La separación importa: el proceso general atiende entradas y realiza la mayor parte del trabajo con una cuenta sin privilegios; el componente reducido ejecuta sólo la acción excepcional y valida qué peticiones acepta. Así una falla en la parte expuesta no hereda automáticamente autoridad administrativa.
Las capacidades de Linux dividen parte de la autoridad tradicional de root en categorías. Eso permite expresar necesidades más estrechas, pero «una sola capacidad» no significa «poco poder». El nombre es un índice, no una medida de riesgo. Antes de conceder, estudia qué comprobaciones omite, sobre qué recursos opera y si permite obtener otras facultades. Documenta la operación concreta que la necesita y crea una prueba que falle cuando se retire; si el servicio sigue funcionando sin ella, la concesión era innecesaria. Revisa también capacidades heredadas, permitidas y efectivas, no sólo una lista en un archivo de despliegue.
Capacidades especialmente amplias. CAP_DAC_OVERRIDE permite saltar muchas comprobaciones normales de acceso a archivos; CAP_SETUID permite cambiar identidades y CAP_SYS_ADMIN agrupa operaciones muy diversas. Pueden acercar el alcance práctico a una cuenta administrativa aunque la configuración muestre una sola entrada. Si una aplicación sólo necesita leer una configuración concreta, ajustar propietario o grupo de ese archivo es más estrecho que permitirle ignorar permisos de muchos archivos. La comparación correcta no cuenta elementos concedidos: compara el conjunto de acciones que cada alternativa vuelve posible.
Método de concesión. Escribe la necesidad como verbo y objeto, por ejemplo «leer la configuración X» o «abrir el recurso Y durante el arranque». Enumera alternativas en orden de menor alcance: cambiar propiedad del objeto, usar un grupo dedicado, preparar el recurso mediante otro componente o conceder una facultad estrecha. Para la alternativa elegida, lista lo que también habilita y cómo se vigilará. Prueba inicio, operación, actualización y apagado. Finalmente elimina concesiones temporales usadas durante el diagnóstico; dejar una autoridad «por si acaso» rompe el razonamiento de mínimo privilegio.
Sistema de archivos por zonas. Código, configuración, datos persistentes, caché y temporales tienen necesidades diferentes. Si un servicio sólo escribe en /var/lib/reportes y en su área temporal, el resto puede exponerse como sólo lectura y abrirse de manera explícita esas rutas. Esta negación por defecto limita cambios accidentales y modificaciones fuera de la función. No basta con separar directorios si el proceso conserva escritura sobre todos. Tampoco conviene volver efímero lo que debe persistir: una política segura entiende la semántica de cada dato, no impone una regla uniforme.
Preparar las rutas escribibles. Cada excepción debe tener dueño, modo, capacidad y ciclo de vida definidos. Un directorio de datos persistentes no debería servir también para ejecutables; un temporal necesita límites y limpieza; una caché debe poder reconstruirse. Verifica qué ocurre al crear archivos nuevos y con qué permisos nacen. Revisa enlaces simbólicos y montajes para confirmar que la ruta permitida no conduce a otra zona. La meta es que una lista breve de destinos represente de verdad todas las escrituras legítimas y que cualquier intento fuera de ella produzca una señal diagnosticable.
Probar sin desactivar. Activa restricciones primero en un entorno representativo y recorre arranque, trabajo normal, rotación de registros, actualización y recuperación. Cuando algo falle, captura la denegación y relaciónala con una operación necesaria. Abre la excepción mínima; no retires toda la capa. Una política puede funcionar durante una demostración y fallar días después cuando llegue una tarea poco frecuente, por eso se prueban también mantenimiento y respaldo. Conserva métricas sobre denegaciones para distinguir una incompatibilidad real de una solicitud que el proceso nunca debió hacer.
Superficie de red. Escuchar en todas las interfaces permite que cualquier red enrutable y autorizada por capas posteriores intente llegar al servicio. Si sólo un proxy del mismo host debe hablar con un panel, el panel puede vincularse al dispositivo lógico llamado lo, que pertenece al propio espacio de red, en vez de anunciarse hacia interfaces externas. Esto reduce exposición aunque una regla de firewall cambie. Si proxy y panel viven en espacios de red separados, esa suposición deja de ser válida y se diseña una red interna explícita. La topología observada decide el enlace; no se copia una receta sin comprobarla.
Escucha y firewall se complementan. Vincular el servicio al alcance mínimo evita que exista una ruta innecesaria; el firewall controla tráfico según origen, destino y política. Mantener ambos permite que el error de una capa no abra por sí solo la exposición completa. Verifica desde el propio host y desde las redes que deberían quedar fuera. Documenta también protocolos de descubrimiento, sockets Unix y puertos auxiliares: endurecer el puerto principal mientras una interfaz de administración escucha en otro lugar deja intacta la superficie importante. Un inventario de listeners es una medición, no una lista escrita una vez.
Aislamiento y contenedores. Un contenedor puede separar procesos, montajes y red, pero no convierte en estrecho un privilegio amplio sobre el host. Montar rutas administrativas, compartir dispositivos o conceder capacidades extensas atraviesa parte del aislamiento. Evalúa el límite real: qué comparte, qué puede escribir y qué identidad representa fuera del contenedor. Una cuenta root dentro de un espacio sin acceso al host puede tener menos alcance que root directo, pero no se asume; se comprueba la configuración completa. El nombre «aislado» no reemplaza el análisis de capacidades efectivas.
Otras capas. Filtros de llamadas al sistema, espacios privados para temporales, restricción de dispositivos, límites de recursos y bloqueo de escalada pueden reducir caminos que la aplicación no usa. Se eligen desde observación de la función y documentación del runtime. Bloquear una llamada sólo porque suena peligrosa puede romper bibliotecas; permitir una familia completa porque una operación la necesita puede ser demasiado amplio. Introduce controles gradualmente, mide denegaciones y conserva una prueba funcional. El hardening no se evalúa por cantidad de directivas, sino por capacidades retiradas con operación legítima preservada.
Dependencias y actualización. Un sistema de archivos de sólo lectura debe contemplar cómo se actualiza el código: normalmente otro actor autorizado prepara una nueva versión mientras el proceso de servicio permanece incapaz de modificarla. Separar ejecución y despliegue mejora atribución. Lo mismo aplica a configuración: si el servicio sólo consume, una identidad administrativa gestiona los cambios y el reinicio los aplica de forma controlada. Conceder escritura permanente al proceso para facilitar una actualización ocasional mezcla dos funciones y amplía lo que un fallo durante la operación cotidiana podría alterar.
Ejemplo de diseño. Un panel genera reportes, guarda resultados en /var/lib/reportes, usa temporales y recibe peticiones únicamente de un proxy vecino. Se ejecuta con cuenta dedicada; código y configuración son inmutables para esa cuenta; datos y temporales son excepciones con límites; la escucha se acota al espacio compartido; y cualquier operación privilegiada excepcional se elimina o separa. Las pruebas cubren arranque, generación, rotación y actualización. Si una dependencia falla, el proceso conserva sólo los caminos necesarios para producir reportes, no autoridad general sobre host y red.
Cómo decidir ante la práctica. Una operación privilegiada concreta invita a una facultad acotada o a separar el componente, no a ejecutar todo con autoridad total. Una petición de CAP_DAC_OVERRIDE para un archivo revela una concesión más amplia que ajustar el acceso a ese objeto. Un servicio con dos destinos legítimos de escritura permite negar escritura en el resto y declarar excepciones. Y un panel consumido desde el mismo espacio de red puede escuchar en lo. En cada caso, la respuesta sale del inventario de función y del radio de impacto, no de contar tecnologías.
Regla práctica: cada excepción de autoridad debe caber en una frase con sujeto, verbo, objeto y momento. Si sólo puedes justificarla con «la aplicación lo pide», todavía falta observar qué operación falla. Si la concesión permanece durante toda la vida para una tarea de arranque, estudia separarla o retirarla después. La precisión de la justificación suele anticipar la precisión del control.
Lista de cierre. Una medida de hardening está justificada cuando nombra la operación que conserva, las capacidades que retira, las excepciones que abre, la prueba que ejecutó y el canal donde aparecerá una denegación. Si la explicación es «una capacidad siempre es segura», falta medir su alcance. Si todo el sistema es inmutable salvo que nadie puede actualizarlo, falta el camino operativo. Si el servicio escucha ampliamente y depende de una sola regla externa, falta defensa en profundidad. Reducir autoridad y conservar función son dos requisitos simultáneos.