Sistemas operativos
OpenBSD: seguridad mediante simplicidad y diseño de sistemas
OpenBSD trata la seguridad como una propiedad del sistema mediante una configuración mínima por defecto, auditoría proactiva, mitigaciones por capas e implementaciones específicas.
OpenBSD es un sistema operativo de tipo Unix derivado de 4.4BSD, pero su modelo de seguridad es más que una colección de ajustes de refuerzo. El proyecto trata la seguridad como una propiedad del sistema completo: mantener comprensible el entorno base, desactivar servicios innecesarios, auditar el código crítico, adoptar interfaces conservadoras y contener los fallos que superen la revisión.
Una filosofía de seguridad y simplicidad
OpenBSD sigue de cerca la filosofía Unix, lo que se traduce en un sistema sencillo de instalar y configurar sin perder de vista la seguridad. Su política de configuración segura por defecto deja desactivados los servicios que no son esenciales. Este enfoque de «menos es más» lleva a habilitar solo lo necesario, lo que reduce tanto el código accesible de forma remota como las posibilidades de cometer errores de configuración.
La simplicidad es una propiedad de seguridad en este diseño: menos mecanismos, dependencias y rutas de compatibilidad implican menos comportamientos que comprender, probar y auditar. OpenBSD omite funciones o sustituye un componente cuando esa decisión deja un sistema más claro y fácil de mantener.
Los archivos de configuración de OpenBSD usan una sintaxis compacta y legible, y las páginas de manual documentan cada opción en detalle. Una documentación ausente o incorrecta constituye un fallo porque ningún administrador puede proteger un comportamiento que no puede identificar y configurar correctamente.
Defensas proactivas y mitigaciones de explotación
OpenBSD combina esa contención con defensas proactivas. Su proceso de auditoría examina repetidamente componentes críticos en busca de fallos de corrección ordinarios, incluso cuando todavía no se sabe si son explotables. También desarrolla mitigaciones que limitan las consecuencias de un fallo de corrupción de memoria que sí llegue a explotarse. Estas capas incluyen:
- Política W^X: OpenBSD aplica estrictamente la regla de que una región de memoria escribible no puede ser también ejecutable. Los programas que necesitan mapas ejecutables y escribibles requieren una excepción explícita tanto en el ejecutable como en la política de montaje del sistema de archivos.
- Protecciones de pila y mitigaciones contra SROP: Las protecciones de pila detectan su corrupción, mientras que las defensas contra SROP restringen estados de retorno de señal falsificados. Ambas eliminan vías que convierten un fallo de memoria en un secuestro del flujo de control.
bcrypt(3)para contraseñas: Este hash de contraseñas basado en Blowfish dispone de un factor de trabajo configurable, de modo que probar contraseñas sin conexión resulte deliberadamente costoso sin recurrir a un hash generalista rápido.- Confinamiento de aplicaciones:
pledge(2)restringe las clases de operaciones del sistema que puede solicitar un proceso, mientras queunveil(2)limita las rutas del sistema de archivos que puede ver. Aplicar ambos a programas del sistema base reduce el daño posible después de un compromiso.
OpenBSD combina estas mitigaciones con privilegios limitados, valores por defecto conservadores y revisión de código. Cada capa elimina una ruta de explotación o limita la autoridad disponible cuando falla otra capa.
Valores por defecto y herramientas orientadas a la seguridad
El enfoque de OpenBSD no se limita a mitigar técnicas de explotación. También prioriza valores por defecto conservadores, utilidades pequeñas y auditables, e integración estrecha entre kernel y entorno base de usuario. Estas decisiones reducen el código accesible y la complejidad administrativa antes de aplicar cualquier refuerzo posterior a la instalación.
Entre los mecanismos y herramientas del sistema base que suelen destacarse en este contexto están:
- KARL (Kernel Address Randomized Link): Después
de cada arranque,
reorder_kernel(8)vuelve a enlazar los objetos del kernel en orden aleatorio para el siguiente arranque. Así cambian las posiciones internas del código, los datos y los gadgets, no solo la dirección de carga del kernel. - Reenlazado de bibliotecas críticas en el arranque: OpenBSD aleatoriza durante el arranque el orden de los objetos de bibliotecas compartidas clave, reduciendo la predictibilidad de ataques basados en desplazamientos fijos.
arc4random(3)ygetentropy(2): Estas interfaces proporcionan datos aleatorios sin obligar a cada llamante a gestionar un generador seudoaleatorio y se emplean de forma coherente en el sistema base.signify(1): Una herramienta sencilla para firmar y verificar artefactos de las versiones publicadas y parches binarios, integrando la verificación de integridad en la administración habitual.doas(1): Una utilidad compacta de elevación de privilegios pensada para ser fácil de auditar y de configurar en flujos administrativos comunes.pf(4)y separación de privilegios: El filtro de paquetes con estado complementa la práctica consolidada de dividir los demonios en procesos con privilegios acotados, lo que limita la autoridad que conserva un proceso comprometido.
La auditoría continua y unas páginas de manual precisas mantienen a la vista el comportamiento relevante para la seguridad: la configuración es explícita, los valores por defecto son deliberados y la administración se apoya en primitivas documentadas.
security(8) y
comprobaciones diarias del sistema
OpenBSD incluye security(8), un
script ejecutado por daily(8) que busca indicios de
debilidades habituales y envía cualquier resultado por correo a root.
Entre otras tareas, revisa las bases de datos de cuentas, los permisos
de directorios personales y archivos de configuración, los cambios en
archivos enumerados en /etc/changelist, las etiquetas y
tablas de particiones de discos montados, los cambios de paquetes y
determinados permisos de configuración de red.
El propio manual lo presenta deliberadamente como una ayuda para la seguridad, no como una protección completa. Su valor consiste en hacer visibles debilidades evidentes y cambios inesperados del sistema, siempre que un administrador revise los informes diarios y mantenga actualizada la lista de archivos supervisados.
Refuerzo adicional: aleatorización y seguridad de memoria
Además de mitigaciones ampliamente conocidas, OpenBSD lleva años invirtiendo en técnicas que reducen la predictibilidad tanto en espacio de usuario como en el kernel, dificultando la explotación fiable. Estas medidas son más efectivas cuando se combinan: incluso con fugas parciales de información, cada capa adicional incrementa el coste de construir cadenas de ataque consistentes.
- Aleatorización de direcciones y disposición: Se emplean distintas capas de aleatorización para reducir la repetibilidad de técnicas que dependen de posiciones estables en memoria.
- Aleatorización del kernel (KARL): Cada kernel reenlazado presenta una disposición interna distinta, lo que niega a los exploits los desplazamientos relativos estables de una misma compilación.
- Aleatorización de PID: Los identificadores de proceso pueden ser menos predecibles, reduciendo el valor de ataques basados en adivinar PIDs.
- Primitivas más seguras en libc: Funciones como
freezero(3)yrecallocarray(3)están diseñadas para ayudar a gestionar datos sensibles y patrones de asignación con mayor seguridad. - Asignador de memoria reforzado y diagnóstico: La implementación de malloc en OpenBSD combina resistencia a la explotación con diagnósticos que exponen clases de errores de memoria durante el desarrollo y la depuración.
OpenBSD expone los controles operativos mediante interfaces documentadas, principalmente sysctl y ficheros de configuración. La política activa permanece visible, en vez de quedar oculta en un estado implícito.
Control del sistema base y sus implementaciones
OpenBSD desarrolla el kernel y el entorno base de usuario en un mismo árbol de fuentes. Este árbol unificado propaga una nueva restricción del kernel a las utilidades, una reorganización en torno a la separación de privilegios al demonio correspondiente y los cambios asociados a su configuración y manual. El administrador recibe un entorno base coherente, no una colección de componentes ensamblados a partir de proyectos independientes.
El proyecto también mantiene por sí mismo una cantidad poco habitual de software de uso común. OpenBSD elige entre escribir, importar, bifurcar o adaptar considerablemente un componente según la claridad de su interfaz, la carga de mantenimiento, la integración y la idoneidad de la licencia. La propia lista de programas e ideas del proyecto permite apreciar el alcance de este trabajo.
El valor para la seguridad procede del método de diseño: eliminar comportamientos innecesarios, mantener el código crítico dentro del proceso de revisión del proyecto y aplicar de forma coherente las mismas primitivas defensivas. El tamaño solo contribuye si el componente resultante sigue siendo correcto, se mantiene y resulta apropiado para su cometido.
El desarrollador de OpenBSD Ted Unangst ha descrito esta práctica como un intento de reducir los fallos futuros y los problemas de mantenimiento, no como una forma de borrar fallos conocidos mediante una reescritura. Una reescritura descarta lecciones poco visibles si sus desarrolladores no las recuperan. Su ventaja debe proceder de un alcance deliberadamente más acotado y de un código que el proyecto espera poder comprender y mantener a largo plazo.
Componentes propios y mantenidos por el proyecto
Estos ejemplos distinguen los motivos de ingeniería para controlar una implementación sin convertir el sistema base en un catálogo indiscriminado de proyectos de OpenBSD:
httpd(8): OpenBSD sustituyó nginx en el sistema base después de que los parches locales de refuerzo, el crecimiento del código, sus propios asignadores de memoria y las funciones de biblioteca duplicadas dificultaran la integración y la revisión. El sustituto se concentra en archivos estáticos, FastCGI y TLS, y se ejecuta en chroot con separación de privilegios. Su valor no reside solo en el tamaño, sino en que utiliza directamente las defensas de libc y la arquitectura habitual de los demonios de OpenBSD.doas(1): Su gramática compacta de reglaspermit/denyespecifica una identidad, el usuario de destino, una orden y sus argumentos exactos, mientras que el programa construye por defecto un entorno controlado. Así, la política de privilegios queda explícita y resulta fácil de revisar; una regla descuidada no pasa por ello a ser segura.signify(1): Esta herramienta compacta verifica las firmas de versiones publicadas y paquetes mediante una interfaz de línea de órdenes acotada y una función deliberadamente limitada, sin necesidad de incorporar al sistema base una implementación generalista de OpenPGP.- OpenSMTPD: Su alcance contenido se refuerza mediante compartimentación entre procesos y API externas de extensión. Las dependencias opcionales permanecen fuera del núcleo del demonio y de su espacio de direcciones.
- LibreSSL: OpenBSD bifurcó OpenSSL en 2014 con el objetivo de modernizar el código, facilitar su auditoría y reparación y retirar funciones y soportes de plataformas obsoletos o defectuosos. Estos objetivos de mantenibilidad reducen el código y las rutas de compatibilidad que requieren revisión de seguridad.
- OpenSSH: OpenBSD mantiene este conjunto de herramientas de acceso remoto, ampliamente desplegado, y lo empleó para introducir la separación de privilegios. Los ports de OpenSSH trasladan esa ingeniería del sistema base a otras plataformas.
init(8) y rcctl(8):
control explícito de servicios
OpenBSD mantiene separadas las capas del arranque del sistema. El
pequeño proceso init(8) entra en modo
multiusuario, gestiona los inicios de sesión por terminal y controla la
transición al apagado o al modo monousuario. Invoca el script de arranque documentado
rc(8), que realiza las tareas generales del sistema e
inicia los demonios seleccionados mediante rc.conf(8). Los
distintos scripts rc.d(8) ofrecen una interfaz común para
iniciar, detener, recargar, reiniciar y comprobar servicios, así como
para validar su configuración.
rcctl(8) no
es el sistema de inicio ni un supervisor de servicios residente. Es una
pequeña interfaz que registra las decisiones locales en
/etc/rc.conf.local e invoca los controles
rc.d(8) existentes. El administrador puede habilitar
explícitamente un demonio, establecer sus argumentos, usuario, clase de
inicio de sesión, tabla de encaminamiento, directorio de trabajo,
registro o tiempo de espera, validar su configuración y enumerar tanto
los servicios habilitados que han fallado como los deshabilitados que
siguen en ejecución.
La habilitación de cada demonio queda representada explícitamente en
la configuración; las modificaciones locales permanecen en un único
archivo de texto que las actualizaciones no sobrescriben, y resulta
sencillo comprobar la identidad y la clase de recursos configuradas
para un demonio. Durante el arranque, rc(8) también sigue
una secuencia visible que incluye establecer el nivel de seguridad del
kernel y efectuar el reenlazado aleatorio. El pequeño plano de control
añade poco estado oculto y expone a inspección directa los servicios
innecesarios o mal configurados.
cron(8): planificación explícita
y verificable
No todos los componentes que mantiene OpenBSD se escribieron desde
cero. Su cron(8) procede de la
implementación de Paul Vixie, pero OpenBSD conserva una versión
integrada en el sistema base. Conservarlo en el sistema base permite a
OpenBSD auditar y adaptar código importado adecuado, en vez de
sustituirlo solo por buscar la novedad.
El planificador trata sus entradas como configuración privilegiada.
Ignora los crontabs de usuario si no tienen el modo 0600,
y nadie salvo root debe poder escribir en /etc/crontab,
que tampoco puede tener bits de ejecución, set-ID o sticky. Los
archivos cron.allow y cron.deny de la
utilidad crontab(1)
controlan quién puede instalar crontabs de usuario, y el acceso se
deniega si no pueden leerse. En el código del
demonio que mantiene el proyecto, pledge(2) limita las
operaciones permitidas al planificador. El código que
prepara los trabajos establece la clase de inicio de sesión y las
credenciales de su propietario y comprueba la autorización de la cuenta
antes de ejecutar la orden.
El formato
crontab(5) de OpenBSD añade controles operativos
específicos en lugar de otro marco de planificación. Un intervalo con
~ elige un desplazamiento aleatorio estable al cargar la
tabla para repartir los inicios simultáneos, mientras que
-s impide que se solapen varias instancias de la misma
entrada. Ambos controles reducen picos de carga, condiciones de carrera
y accesos simultáneos accidentales a un estado compartido. No aíslan la
orden programada: cada trabajo se ejecuta intencionadamente con la
autoridad de su propietario y debe protegerse como cualquier otra
orden.
smtpd(8): separación de
privilegios como arquitectura
OpenSMTPD se escribió como una implementación de SMTP bajo licencia ISC después de que sus desarrolladores no quedaran satisfechos con las alternativas disponibles. Sus objetivos publicados reúnen tres requisitos: validación estricta y separación de privilegios, tratamiento fiable de todos los mensajes aceptados y una implementación contenida, dirigida a los casos de uso habituales y no a los más excepcionales. Un lenguaje de configuración legible forma parte de esos objetivos porque un MTA fácil de configurar mal no ofrece seguridad operativa.
La diferencia está en la arquitectura. En una presentación
de uno de sus desarrolladores, OpenSMTPD se describe como un
conjunto de procesos con cometidos bien definidos, en vez de un único
demonio donde cada subsistema disponga de todos los privilegios. Los
procesos de trabajo se comunican mediante imsg(3), no por
memoria compartida; el trabajo privilegiado se concentra en el proceso
padre, la mayoría de los demás procesos carecen de privilegios y se
ejecutan en chroot, y la cola utiliza una cuenta distinta.
pledge(2) restringe las operaciones permitidas a cada
proceso de trabajo, mientras que la reejecución proporciona a los
procesos hijos disposiciones de memoria independientes de la del
proceso padre inicial.
OpenSMTPD no necesita incorporar al núcleo de confianza todos los
clientes de bases de datos o directorios. La API actual smtpd-tables(7)
ejecuta los módulos de consulta como procesos independientes fuera del
espacio de direcciones de smtpd(8) y permite asignarles
cuentas distintas. Así se refuerzan mutuamente el minimalismo y la
extensibilidad: las dependencias específicas de cada instalación siguen
estando disponibles sin recibir todos los privilegios del demonio ni
formar parte de su frontera de seguridad de memoria.
El propio historial de avisos de seguridad de OpenSMTPD incluye fallos graves, entre ellos errores corregidos en 2020 que permitían ejecutar órdenes. Las fronteras de proceso no eliminan esos fallos; limitan la autoridad asignada a cada función expuesta. La ventaja defendible es esta división explícita y revisable de responsabilidades.
vmm(4)/vmd(8): un
hipervisor acotado
La pila de virtualización nativa de OpenBSD separa el monitor de máquinas virtuales
vmm(4) integrado en el kernel, el demonio vmd(8) en espacio de
usuario y el cliente de control vmctl(8). Por tanto,
la emulación de dispositivos permanece principalmente en espacio de
usuario, en vez de colocar en el kernel anfitrión los analizadores de
dispositivos de red y de disco. vmd(8) también inicia un
proceso de máquina virtual distinto para cada invitado.
Los dispositivos emulados procesan datos del invitado controlados
por un posible atacante y han permitido en repetidas ocasiones escapar
de distintos hipervisores. El
análisis de vmd(8) realizado por un desarrollador de
OpenBSD describe su separación de privilegios mediante bifurcación
y nueva ejecución, sus funciones repartidas entre procesos, la
reducción de identificadores de usuario y grupo, el aislamiento del
sistema de archivos y las restricciones de pledge(2).
OpenBSD 7.4 introdujo después
un modelo multiproceso para los dispositivos VirtIO de bloques y red
emulados. Separarlos obliga a un atacante que comprometa uno de los
emuladores a atravesar otra frontera de proceso antes de alcanzar el
resto de vmd(8) o el código de otros dispositivos.
La sección sobre
virtualización de las FAQ también documenta un conjunto de
funciones deliberadamente limitado: no ofrece gráficos, instantáneas,
acceso directo al hardware, migración en vivo entre anfitriones,
cambios de hardware en vivo ni SMP para los invitados. Menos rutas de
emulación y migración implican menos código y estado que mantener. Las
FAQ no identifican la seguridad como motivo de cada omisión. La
frontera de confianza del anfitrión sigue incluyendo
vmm(4), vmd(8) y el kernel; un fallo
explotable en esos componentes puede provocar un escape. El diseño más
pequeño e integrado con OpenBSD aplica compartimentación en el lado del
anfitrión.
openrsync(1): compatibilidad
sin incorporar rsync
openrsync es una
implementación desde cero de rsync, bajo licencia ISC y escrita por
Kristaps Dzonsons. Nació como parte de rpki-client(8), que
necesitaba obtener datos de repositorios RPKI mediante el protocolo
rsync, y se importó
en OpenBSD en 2019. El resultado proporcionó al sistema base una
implementación bajo la licencia preferida y el control directo del
proyecto; no era una mera copia renombrada del rsync original.
Su compatibilidad es deliberadamente más limitada que la de un sustituto completo de rsync. El manual actual define las opciones que admite, la sincronización local y remota y la interoperabilidad mediante la versión 27 del protocolo rsync. OpenBSD también proporciona la documentación de los protocolos rsync(5) y rsyncd(5), de modo que los formatos de comunicación quedan suficientemente explícitos para revisarlos e implementarlos de forma independiente. Los flujos que requieran protocolos más nuevos u opciones no admitidas siguen necesitando el rsync original.
En OpenBSD, el confinamiento limita la autoridad de openrsync. El
main.c
actual emplea pledge(2) para limitar las operaciones
del sistema y restringe aún más sus promesas después de preparar la
red. En el lado receptor, receiver.c
aplica unveil(2) en torno a las rutas de origen y la raíz
de destino. En modo servidor, server.c
utiliza arc4random(3), en vez de la hora, para inicializar
los hashes MD4 del protocolo.
Una interfaz más acotada deja menos comportamiento que comprender, la documentación del protocolo facilita su revisión y mantenimiento y el confinamiento limita aquello a lo que puede acceder un fallo. Las fuentes disponibles no demuestran que openrsync tenga menos vulnerabilidades que rsync. Sus funciones normales de cliente y servidor no constituyen un diseño independiente de separación de privilegios.
Auditoría, programación segura y documentación
OpenBSD describe su proceso de auditoría como un examen archivo por archivo del software crítico. Los revisores buscan fallos ordinarios sin esperar a que exista una explotación demostrada y vuelven a examinar código ya auditado cuando se comprende una nueva clase de error. Así se evita tratar los informes de vulnerabilidades como única fuente de trabajo de seguridad.
El mismo enfoque rige interfaces de programación como
reallocarray(3), recallocarray(3),
freezero(3) y explicit_bzero(3). Estas API
facilitan expresar comprobaciones de desbordamiento de enteros o el
borrado fiable de memoria sensible. No convierten C en un lenguaje con
seguridad de memoria, pero transforman patrones defensivos habituales
en primitivas revisadas del sistema.
Unas páginas de manual precisas completan el modelo. Una implementación difícil de describir también resulta difícil de operar y auditar. Considerar un defecto la documentación inexacta mantiene el comportamiento relevante para la seguridad a la vista de desarrolladores y administradores, en vez de esconderlo tras convenciones.
Conclusiones
OpenBSD reduce aquello en lo que hay que confiar, hace comprensible el comportamiento restante, lo audita de forma repetida y contiene los fallos con defensas por capas. Sus implementaciones propias, entre ellas el uso directo de las defensas del sistema por parte de httpd, la arquitectura dividida en procesos de OpenSMTPD, la compartimentación del lado del anfitrión de vmm/vmd, el plano explícito de control de init/rc y el objetivo acotado de compatibilidad de openrsync, muestran que esta filosofía se refleja en partes sustanciales del sistema base y no se añade después como un conjunto opcional de ajustes de refuerzo.