HIPAA es la ley estadounidense que regula la protección de la información sanitaria de los pacientes. Su Security Rule obliga a quien almacena o procesa información sanitaria electrónica (ePHI) a aplicar salvaguardas administrativas, físicas y técnicas. En este tutorial implementarás en Ubuntu 24.04 las salvaguardas técnicas de la sección §164.312 y la parte técnica del plan de contingencia: un volumen cifrado para los datos, acceso por roles, auditoría de cada acceso, cierre de sesiones inactivas, cifrado en tránsito y copias de seguridad cifradas con pruebas de restauración.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS y un usuario no root con privilegios sudo.
  • Un segundo disco vacío para los datos (en esta guía, /dev/vdb). Cifrar el disco del sistema de un servidor remoto exige introducir la clave en cada arranque desde la consola, así que la práctica habitual es cifrar un volumen de datos independiente.
  • Un segundo servidor o almacenamiento accesible por SFTP para las copias de seguridad (paso 6).
  • Opcional: Nginx con un certificado TLS si el servidor expone una aplicación web (paso 5).

Paso 1: Relacionar los requisitos con controles

Requisito HIPAASecciónControl en esta guía
Identificación única de usuario§164.312(a)(2)(i)Cuentas nominales y grupos por rol (paso 3)
Cierre automático de sesión§164.312(a)(2)(iii)TMOUT en la shell (paso 3)
Cifrado y descifrado§164.312(a)(2)(iv)Volumen LUKS (paso 2)
Controles de auditoría§164.312(b)Reglas de auditd sobre los datos (paso 4)
Integridad y seguridad en la transmisión§164.312(c), (e)TLS 1.2 o superior (paso 5)
Copia de seguridad y recuperación§164.308(a)(7)restic cifrado y prueba de restauración (paso 6)

Paso 2: Crear un volumen cifrado con LUKS

LUKS cifra el dispositivo de bloques completo con AES. Si alguien obtiene una copia del disco, sin la frase de paso no puede leer nada.

Identifica el disco vacío:

lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
vda     253:0    0   40G  0 disk
├─vda1  253:1    0   39G  0 part /
└─vda15 253:15   0  105M  0 part /boot/efi
vdb     253:16   0  100G  0 disk

Formatea el disco con LUKS2. Te pedirá que escribas YES en mayúsculas y una frase de paso larga; guárdala en tu gestor de contraseñas corporativo:

sudo apt install cryptsetup
sudo cryptsetup luksFormat --type luks2 /dev/vdb

Abre el volumen, crea el sistema de archivos y móntalo:

sudo cryptsetup open /dev/vdb ephi
sudo mkfs.ext4 /dev/mapper/ephi
sudo mkdir -p /srv/ephi
sudo mount /dev/mapper/ephi /srv/ephi

Verifica que el volumen está cifrado y montado:

sudo cryptsetup status ephi
findmnt /srv/ephi
/dev/mapper/ephi is active and is in use.
  type:    LUKS2
  cipher:  aes-xts-plain64
  keysize: 512 bits
...
TARGET    SOURCE           FSTYPE OPTIONS
/srv/ephi /dev/mapper/ephi ext4   rw,relatime

La frase de paso no se guarda en el servidor, así que tras cada reinicio tendrás que abrir y montar el volumen manualmente. Es el comportamiento deseado: un disco o una copia del servidor robados no exponen los datos. Tras un reinicio:

sudo cryptsetup open /dev/vdb ephi
sudo mount /dev/mapper/ephi /srv/ephi
sudo augenrules --load

El último comando recarga las reglas de auditoría del paso 4 para que vigilen el volumen recién montado.

Paso 3: Controlar quién accede y cerrar sesiones inactivas

Grupos por rol

Cada persona debe tener su propia cuenta; nada de usuarios compartidos. Crea un grupo con acceso de lectura y otro de lectura y escritura:

sudo groupadd ephi-ro
sudo groupadd ephi-rw

Asigna el directorio a root y al grupo de escritura, sin acceso para el resto. El bit setgid (el 2 de 2770) hace que los ficheros nuevos hereden el grupo:

sudo chown root:ephi-rw /srv/ephi
sudo chmod 2770 /srv/ephi

Da acceso de lectura al grupo ephi-ro con ACL, también por defecto para los ficheros que se creen después:

sudo apt install acl
sudo setfacl -m g:ephi-ro:rX -m d:g:ephi-ro:rX -m d:g:ephi-rw:rwX /srv/ephi
getfacl /srv/ephi
# file: srv/ephi
# owner: root
# group: ephi-rw
# flags: -s-
user::rwx
group::rwx
group:ephi-ro:r-x
mask::rwx
other::---
default:user::rwx
default:group::rwx
default:group:ephi-ro:r-x
default:group:ephi-rw:rwx
default:mask::rwx
default:other::---

Añade a cada usuario al grupo que le corresponda y documenta quién aprobó el acceso:

sudo usermod -aG ephi-ro clinical_user
sudo usermod -aG ephi-rw app_admin

Si los datos los gestiona una aplicación (una base de datos, por ejemplo), lo normal es que solo el usuario de servicio de esa aplicación tenga acceso al directorio y que las personas accedan a través de la aplicación, que es la que debe registrar quién ve cada historial.

Cierre de sesión por inactividad

Una idea extendida es que ClientAliveInterval de SSH cierra las sesiones inactivas. No es así: solo detecta conexiones muertas, y un cliente conectado pero sin actividad responde a esos mensajes. La forma de cerrar una shell inactiva es la variable TMOUT de Bash:

sudo nano /etc/profile.d/99-tmout.sh
# Cierra la shell tras 15 minutos sin actividad
TMOUT=900
readonly TMOUT
export TMOUT

Abre una sesión nueva y comprueba el valor:

echo $TMOUT
900

HIPAA no fija un tiempo concreto; 10 a 15 minutos es lo habitual. Documenta el valor elegido en tu política.

Paso 4: Auditar cada acceso a ePHI

El requisito de controles de auditoría pide registrar la actividad en los sistemas que contienen ePHI. auditd puede registrar cada lectura, escritura o cambio de atributos en el volumen de datos.

sudo apt install auditd audispd-plugins
sudo nano /etc/audit/rules.d/50-ephi.rules
## Lectura, escritura y cambios de atributos en los datos de pacientes
-w /srv/ephi -p rwa -k ephi-access

## Cambios en cuentas y grupos
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k identity
-w /etc/sudoers.d -p wa -k identity

Una vigilancia (-w) sobre un directorio incluye todo su contenido. Carga las reglas y compruébalas:

sudo augenrules --load
sudo auditctl -l

Genera un acceso de prueba y búscalo. La opción -i traduce UID y llamadas al sistema a nombres legibles:

sudo touch /srv/ephi/prueba.txt
sudo cat /srv/ephi/prueba.txt
sudo ausearch -k ephi-access -ts recent -i | grep -E 'type=SYSCALL' | tail -n 2

En cada evento verás auid (el usuario que inició sesión, aunque luego usara sudo), comm y exe (el programa) y name en el registro PATH asociado (el fichero).

Para la revisión periódica que exige la norma, genera un resumen de ficheros accedidos y otro de usuarios en el mes actual:

sudo ausearch -k ephi-access --start this-month --raw | sudo aureport -f -i --summary
sudo ausearch -k ephi-access --start this-month --raw | sudo aureport -u -i --summary

La normativa exige conservar la documentación de cumplimiento durante 6 años. Envía los registros de auditoría a un sistema central de logs en lugar de guardarlos solo en el propio servidor, donde un atacante con privilegios podría borrarlos.

Paso 5: Cifrar los datos en tránsito

Cualquier servicio que envíe ePHI por la red debe usar TLS 1.2 o superior. Si publicas una aplicación con Nginx, fija los protocolos en el bloque server que escucha en el puerto 443:

sudo nano /etc/nginx/sites-available/your_domain
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

Comprueba la configuración y recarga:

sudo nginx -t
sudo systemctl reload nginx

Verifica desde tu equipo que TLS 1.3 funciona y que TLS 1.1 se rechaza:

openssl s_client -connect your_domain:443 -tls1_3 </dev/null 2>/dev/null | grep -E '^ *Protocol'
openssl s_client -connect your_domain:443 -tls1_1 </dev/null
    Protocol  : TLSv1.3

El segundo comando debe terminar con un error de handshake. Aplica el mismo criterio a bases de datos, colas o APIs internas que muevan ePHI entre servidores, aunque vayan por red privada.

Paso 6: Copias de seguridad cifradas con restic

El plan de contingencia (§164.308(a)(7)) exige copias exactas y recuperables de la ePHI. restic cifra los datos en el origen, antes de enviarlos, así que el servidor de copias nunca ve la información en claro.

sudo apt install restic

Guarda la contraseña del repositorio en un fichero solo legible por root. Genera una aleatoria:

sudo sh -c 'openssl rand -base64 32 > /root/.restic-ephi'
sudo chmod 600 /root/.restic-ephi

Inicializa el repositorio en el servidor de copias por SFTP. root necesita poder entrar por clave SSH en your_backup_host:

sudo restic -r sftp:backup@your_backup_host:/srv/restic/ephi --password-file /root/.restic-ephi init
created restic repository 4f8a2c1d9e at sftp:backup@your_backup_host:/srv/restic/ephi

Crea un servicio de systemd que haga la copia, aplique la retención y verifique el repositorio:

sudo nano /etc/systemd/system/ephi-backup.service
[Unit]
Description=Copia cifrada de ePHI con restic
RequiresMountsFor=/srv/ephi

[Service]
Type=oneshot
Environment=RESTIC_REPOSITORY=sftp:backup@your_backup_host:/srv/restic/ephi
Environment=RESTIC_PASSWORD_FILE=/root/.restic-ephi
ExecStart=/usr/bin/restic backup /srv/ephi --tag ephi
ExecStart=/usr/bin/restic forget --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
ExecStart=/usr/bin/restic check

RequiresMountsFor evita que la copia se ejecute si el volumen cifrado no está montado. Añade el temporizador diario:

sudo nano /etc/systemd/system/ephi-backup.timer
[Unit]
Description=Copia diaria de ePHI

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true

[Install]
WantedBy=timers.target

Actívalo, lanza una copia ahora y revisa el resultado:

sudo systemctl daemon-reload
sudo systemctl enable --now ephi-backup.timer
sudo systemctl start ephi-backup.service
sudo journalctl -u ephi-backup.service -n 20
...
snapshot 7c1e9b2a saved
...
no errors were found

Probar la restauración

Una copia sin prueba de restauración no cuenta como evidencia. Restaura la última copia en un directorio temporal y compárala con los datos actuales:

sudo restic -r sftp:backup@your_backup_host:/srv/restic/ephi --password-file /root/.restic-ephi \
  restore latest --target /root/restore-test
sudo diff -r /srv/ephi /root/restore-test/srv/ephi && echo "Restauración correcta"
sudo rm -rf /root/restore-test

Repite esta prueba al menos una vez al trimestre y anota en tu registro de cumplimiento la fecha, la copia restaurada y el resultado.

Solución de problemas

ausearch no devuelve eventos de ephi-access. Si montaste el volumen después de cargar las reglas, la vigilancia apunta al directorio vacío de debajo del punto de montaje. Ejecuta sudo augenrules --load con el volumen ya montado.

La copia falla con Fatal: unable to open repository. Comprueba que root puede conectar por SSH al servidor de copias sin contraseña con sudo ssh backup@your_backup_host y que la ruta existe.

La sesión se cierra mientras trabajas. TMOUT solo actúa cuando la shell espera un comando en el prompt; un comando en ejecución, como una copia larga, no se interrumpe. Si el tiempo resulta demasiado corto, cámbialo en /etc/profile.d/99-tmout.sh y en tu política, en lugar de buscar formas de saltárselo.

Olvidaste la frase de paso de LUKS. No hay forma de recuperar los datos del volumen. Restaura desde restic en un volumen nuevo, y añade una segunda frase de paso de emergencia guardada en custodia con sudo cryptsetup luksAddKey /dev/vdb.

Conclusión

Tu servidor Ubuntu 24.04 guarda ahora la ePHI en un volumen cifrado con LUKS, limita el acceso por roles, registra cada lectura y modificación con auditd, cierra las sesiones inactivas, exige TLS 1.2 o superior y hace copias cifradas y verificadas con restic. Como siguientes pasos, completa el análisis de riesgos documentado que exige §164.308(a)(1), centraliza los registros de auditoría en un sistema de logs y define el procedimiento de respuesta ante incidentes y notificación de brechas.