1. El editor de flujo sed
La aplicación de una línea base de seguridad, como el CIS Benchmark, sobre decenas o cientos de servidores no puede depender de editores interactivos. La utilidad sed (Stream Editor) transforma texto de forma programática y desatendida, lo que la convierte en una pieza habitual de:
-
Corrección automática de configuraciones: Corrección de parámetros inseguros en scripts de aprovisionamiento, imágenes de máquinas virtuales o contenedores.
-
Normalización de configuraciones: Homogeneización de directivas en una flota de servidores y eliminación de valores por defecto inseguros.
-
Tratamiento de datos en tuberías: Reestructuración de la salida de herramientas de auditoría y seudonimización de registros antes de enviarlos a un SIEM o compartirlos con terceros.
|
En entornos con gestión de la configuración, como Ansible, los módulos |
1.1. Arquitectura interna y ciclo de ejecución
sed no carga el fichero completo en memoria. Procesa la entrada línea a línea mediante dos áreas de trabajo:
-
Espacio de patrones o pattern space: Contiene la línea actual. Los comandos del programa se aplican sobre él.
-
Espacio de retención o hold space: Área auxiliar que conserva datos entre ciclos y permite operaciones que afectan a varias líneas, mediante los comandos
h,H,g,Gyx.
En cada ciclo, sed lee una línea sin su salto de línea final, ejecuta todos los comandos cuya dirección coincide y, salvo que se haya indicado -n, imprime el espacio de patrones antes de pasar a la línea siguiente.
2. Sintaxis, opciones y sustitución
sed [opciones] 'programa' [fichero...]
sed [opciones] -e 'comando1' -e 'comando2' [fichero...]
sed [opciones] -f programa.sed [fichero...]
| Opción | Efecto | Observaciones de seguridad |
|---|---|---|
|
Suprime la impresión automática |
Se combina con |
|
Usa expresiones regulares extendidas |
Mismo dialecto que |
|
Edita el fichero en su lugar, con copia de seguridad opcional |
Ver la sección 4 antes de usarla en producción |
|
Trata cada fichero como un flujo independiente |
Sin ella, |
|
Separa los registros por el carácter nulo |
Procesa la salida de |
|
Muestra el programa interpretado y el estado de cada ciclo |
Disponible desde GNU sed 4.6 |
2.1. El comando de sustitución s
s/patrón/reemplazo/modificadores
| Modificador | Efecto |
|---|---|
|
Sustituye todas las coincidencias de la línea, no solo la primera |
|
Sustituye solo la coincidencia número |
|
Imprime la línea si hubo sustitución; se usa con |
|
Ignora mayúsculas y minúsculas; extensión de GNU |
|
Escribe en |
En el texto de reemplazo, & representa toda la coincidencia y \1 a \9 los grupos capturados. GNU añade \U y \L para convertir a mayúsculas o minúsculas:
# Seudonimizar los dos últimos octetos de las direcciones IPv4 antes de compartir un registro
sed -E 's/([0-9]{1,3}\.[0-9]{1,3})\.[0-9]{1,3}\.[0-9]{1,3}/\1.x.x/g' auth.log
# Normalizar a minúsculas el valor de una directiva
echo 'PermitRootLogin No' | sed -E 's/^(PermitRootLogin[[:blank:]]+)(.*)/\1\L\2/'
|
La seudonimización de direcciones IP antes de compartir registros con proveedores o en informes forma parte de las medidas de minimización de datos que exige el RGPD, ya que una dirección IP puede constituir un dato personal. |
2.2. Delimitadores alternativos
Cuando el patrón o el reemplazo contienen /, como ocurre con rutas, cualquier carácter que siga a la s puede actuar como delimitador, lo que evita escapar cada barra:
# Ambas órdenes son equivalentes; la segunda es mucho más legible
sed 's/\/usr\/local\/bin/\/opt\/bin/' rutas.txt
sed 's#/usr/local/bin#/opt/bin#' rutas.txt
|
Con los conceptos explicados hasta aquí, ya puede realizar el Ejercicio 1. Preparación del laboratorio y selección de líneas y el Ejercicio 2. Sustituciones y retrorreferencias. |
3. Direccionamiento y comandos
Sin dirección, un comando se aplica a todas las líneas. Una dirección limita su ámbito:
| Dirección | Selecciona | Ejemplo |
|---|---|---|
|
La línea |
|
|
La última línea |
|
|
Las líneas que coinciden con la expresión |
|
|
Desde |
|
|
La línea |
|
|
Desde el principio hasta la primera coincidencia; extensión de GNU |
|
|
Todas las líneas que no cumplen la dirección |
|
Comandos de edición más habituales:
| Comando | Efecto |
|---|---|
|
Imprime el espacio de patrones |
|
Elimina la línea y empieza el ciclo siguiente |
|
Termina tras imprimir la línea actual; GNU permite devolver el código de salida |
|
Añade una línea después de la actual |
|
Inserta una línea antes de la actual |
|
Sustituye la línea completa |
|
Muestra la línea con los caracteres no imprimibles visibles, como |
|
Transliteración carácter a carácter, equivalente a |
# Mostrar el bloque Match y todo lo que le sigue en la configuración de SSH
sed -n '/^Match /,$p' /etc/ssh/sshd_config
# Detectar retornos de carro de Windows en un script
sed -n '1,3l' script.sh
# Insertar una cabecera de trazabilidad antes de la primera línea
sed '1i # Gestionado por el script de bastionado. No editar manualmente.' fichero.conf
|
La sintaxis en una sola línea de |
4. Edición in situ y buenas prácticas
-i sobrescribe el fichero original: es una operación destructiva y, sin copia de seguridad, irreversible.
4.1. Funcionamiento real de -i
GNU sed no modifica el fichero existente, sino que escribe el resultado en un fichero temporal del mismo directorio y lo renombra sobre el original. Este comportamiento tiene consecuencias que el auditor debe conocer:
-
El fichero obtiene un nuevo inodo. Se rompen los enlaces duros, y los procesos o contenedores que tienen montado el fichero individualmente siguen viendo la versión antigua.
-
Los enlaces simbólicos se sustituyen por ficheros regulares, salvo que se use
--follow-symlinks. -
Se necesita permiso de escritura en el directorio, no solo en el fichero.
-
Los ficheros con el atributo inmutable activado con
chattr +ino pueden reemplazarse y la operación falla, lo que es precisamente el objetivo de ese atributo. -
GNU
sedintenta conservar el propietario, los permisos, las ACL y el contexto SELinux. Aun así, debe comprobarse constattras cada remediación, porque un fichero como/etc/shadowcon permisos alterados es en sí mismo un incumplimiento.
4.2. Copias de seguridad
sudo sed -i.bak -E 's/^[[:blank:]]*PASS_MAX_DAYS[[:blank:]].*/PASS_MAX_DAYS\t365/' /etc/login.defs
diff /etc/login.defs.bak /etc/login.defs
|
Errores frecuentes con
|
4.3. Ficheros que nunca deben editarse con sed
|
No modifique con |
4.4. Validación antes de aplicar
Toda remediación sobre un servicio debe validarse antes de recargarlo, para no provocar una denegación de servicio ni perder el acceso remoto:
sudo sshd -t && sudo systemctl reload ssh
sudo nginx -t && sudo systemctl reload nginx
sudo visudo -c
|
Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 3. Edición in situ, inodos y copias de seguridad. |
5. Remediación idempotente
Un script de remediación es idempotente cuando ejecutarlo una o varias veces produce el mismo resultado. Una sustitución simple no lo es por completo: si la directiva no existe en el fichero, sed no hace nada y el control queda sin aplicar. El patrón profesional consiste en sustituir si existe y añadir si no existe:
# fijar_directiva CLAVE VALOR FICHERO
fijar_directiva() {
local clave="$1" valor="$2" fichero="$3"
if grep -qE "^[[:blank:]]*${clave}[[:blank:]]" "$fichero"; then
sudo sed -i.bak -E "s|^[[:blank:]]*${clave}[[:blank:]].*|${clave} ${valor}|" "$fichero"
else
echo "${clave} ${valor}" | sudo tee -a "$fichero" > /dev/null
fi
grep -E "^${clave}[[:blank:]]" "$fichero"
}
fijar_directiva PASS_MAX_DAYS 365 /etc/login.defs
fijar_directiva UMASK 027 /etc/login.defs
La función solo modifica directivas activas: no descomenta líneas, porque en ficheros como login.defs muchos comentarios explicativos empiezan por el nombre de la directiva y acabarían convertidos en directivas duplicadas. Termina mostrando la línea resultante, que actúa como verificación inmediata. Los valores no se escapan, por lo que solo deben provenir del propio script y nunca de datos externos.
5.1. El caso de OpenSSH: ficheros incluidos y precedencia
En OpenSSH prevalece el primer valor leído, y en Debian y Ubuntu los ficheros de /etc/ssh/sshd_config.d/ se leen antes que el resto de sshd_config. Por ello, modificar con sed el fichero principal puede no tener ningún efecto. La estrategia robusta es:
-
Crear un fichero propio que se lea en primer lugar por su orden alfabético.
-
Neutralizar con
sedlas directivas contradictorias de otros ficheros incluidos. -
Validar la sintaxis, recargar el servicio y verificar la configuración efectiva.
printf '%s\n' 'PermitRootLogin no' 'PermitEmptyPasswords no' 'MaxAuthTries 4' \
| sudo tee /etc/ssh/sshd_config.d/00-bastionado.conf > /dev/null
sudo sed -i.bak -E 's/^([[:blank:]]*PasswordAuthentication[[:blank:]]+yes)/# \1/I' \
/etc/ssh/sshd_config.d/50-cloud-init.conf
sudo sshd -t && sudo systemctl reload ssh
sudo sshd -T | grep -E '^(permitrootlogin|permitemptypasswords|maxauthtries|passwordauthentication) '
|
Antes de desactivar |
|
Los cambios en |
|
Con los conceptos explicados en esta sección, ya puede realizar el Ejercicio 4. Remediación idempotente. |
6. Recetario de sed para el bastionado
-
Configuración activa sin comentarios ni líneas vacías, sin modificar el original:
sed -E '/^[[:space:]]*(#|;|$)/d' /etc/ssh/sshd_config -
Caducidad máxima de las contraseñas en
/etc/login.defs: El CIS Benchmark para Ubuntu exige un valor de 365 días o inferior. Muchas políticas corporativas fijan 90, aunque la guía NIST SP 800-63B desaconseja los cambios periódicos obligatorios si no hay indicios de compromiso.sudo sed -i.bak -E 's/^[[:blank:]]*PASS_MAX_DAYS[[:blank:]].*/PASS_MAX_DAYS\t365/' /etc/login.defs -
Máscara de creación de ficheros
UMASK 027: Las cuentas nuevas crearán ficheros sin permisos para el resto de usuarios y sin escritura para el grupo, como exige el CIS Benchmark.sudo sed -i.bak -E 's/^[[:blank:]]*UMASK[[:blank:]].*/UMASK\t\t027/' /etc/login.defs -
Comentar una directiva insegura dejando constancia del cambio:
sudo sed -i.bak -E 's/^([[:blank:]]*X11Forwarding[[:blank:]]+yes)/# \1 # Deshabilitado por bastionado/I' /etc/ssh/sshd_config -
Eliminar los retornos de carro de un script creado en Windows:
sed -i 's/\r$//' script_bastionado.sh -
Extraer los eventos de un intervalo temporal de un registro:
sudo sed -n '/^Oct 18 08:00/,/^Oct 18 09:00/p' /var/log/auth.log -
Comprobar un cambio antes de aplicarlo: Ejecute siempre la orden sin
-iy compare el resultado condiff:sed -E 's/^[#[:blank:]]*PermitRootLogin[[:blank:]].*/PermitRootLogin no/' /etc/ssh/sshd_config | diff /etc/ssh/sshd_config -
7. Ejercicios prácticos
Los ejercicios deben realizarse en orden sobre una máquina virtual Debian o Ubuntu de pruebas. Todas las modificaciones se aplican sobre copias situadas en ~/lab_filtrado/sed, de modo que la configuración real del sistema no se altera. El último ejercicio elimina el laboratorio.
| Nº | Ejercicio | Competencia que desarrolla |
|---|---|---|
1 |
Usar |
|
2 |
Construir sustituciones con grupos y delimitadores alternativos |
|
3 |
Comprender los efectos reales de |
|
4 |
Aplicar controles CIS de forma repetible y verificable |
|
5 |
Revertir el laboratorio |
7.1. Ejercicio 1. Preparación del laboratorio y selección de líneas
Realice los siguientes pasos:
-
Cree el laboratorio con una copia de
/etc/login.defs, una configuración de SSH de muestra y un script con finales de línea de Windows:mkdir -p ~/lab_filtrado/sed/sshd_config.d cd ~/lab_filtrado/sed cp /etc/login.defs login.defs cat > sshd_config << 'EOF' Include /etc/ssh/sshd_config.d/*.conf #Port 22 #PermitRootLogin prohibit-password PermitRootLogin yes X11Forwarding yes PermitEmptyPasswords no Match User invitado X11Forwarding no PermitRootLogin yes EOF echo 'PasswordAuthentication yes' > sshd_config.d/50-cloud-init.conf printf '#!/bin/bash\r\necho "bastionado"\r\n' > script_windows.sh -
Muestre únicamente las líneas 1 a 5 de
sshd_configy, después, solo la última línea. -
Muestre el bloque
Matchcompleto y, a continuación, todas las líneas delogin.defsrelacionadas conPASS_que no estén comentadas. -
Utilice el comando
lpara demostrar quescript_windows.shcontiene retornos de carro. Concédale permiso de ejecución, ejecútelo con./script_windows.shy explique el error.
7.2. Ejercicio 2. Sustituciones y retrorreferencias
Realice los siguientes pasos:
-
Sin modificar el fichero, sustituya en
sshd_configel valor dePermitRootLoginpornosolo fuera del bloqueMatch. Compruebe el resultado condiff. -
Genere una versión seudonimizada de esta línea de registro que conserve solo los dos primeros octetos de la dirección IP:
echo 'Failed password for root from 203.0.113.45 port 51234 ssh2' > evento.log -
Usando un delimitador alternativo, sustituya en la salida la ruta
/etc/ssh/sshd_config.d/*.confde la directivaIncludepor/etc/ssh/bastionado.d/*.conf. -
Ejecute el apartado 1 con
--debugy observe cómo se evalúa la dirección en cada ciclo.
7.3. Ejercicio 3. Edición in situ, inodos y copias de seguridad
Realice los siguientes pasos:
-
Anote el inodo de
script_windows.shconls -i, elimine los retornos de carro consed -i.baky compruebe que el inodo ha cambiado, que existe la copia.baky que el script ya se ejecuta correctamente con./script_windows.sh. -
Cree un enlace simbólico
enlace.confque apunte alogin.defs, apliquesed -isobre el enlace sin--follow-symlinksy compruebe conls -len qué se ha convertido. Repita el proceso con--follow-symlinks. -
Ejecute
sed -iE 's/(yes)/no/' sshd_configy explique qué ficheros aparecen y por qué no se ha producido ninguna sustitución. Restaure el original.
7.4. Ejercicio 4. Remediación idempotente
Realice los siguientes pasos:
-
Defina la función
fijar_directivade la sección 5, sustituyendosudo sedysudo teeporsedytee, ya que trabajará sobre copias de su propiedad. Aplique sobrelogin.defslos valoresPASS_MAX_DAYS 365,UMASK 027ySHA_CRYPT_MIN_ROUNDS 5000, que en Debian y Ubuntu solo aparece comentada. Compruebe que las líneas de comentario que mencionanPASS_MAX_DAYSno se han modificado. -
Calcule el hash SHA-256 de
login.defs, ejecute de nuevo las tres llamadas y compruebe que el hash no cambia, lo que demuestra la idempotencia. -
Aplique sobre la copia de SSH la estrategia de la sección 5.1: cree
sshd_config.d/00-bastionado.confconPermitRootLogin noy comente consedla directiva de50-cloud-init.conf. Muestre después todas las directivas activas desshd_config.d/en el orden en que las leería el servicio.
7.5. Ejercicio 5. Limpieza del entorno
-
Elimine el directorio del laboratorio y la función definida en la sesión.
8. Recursos y referencias
-
GNU sed Manual: Referencia oficial del direccionamiento, los comandos multilínea y las extensiones de GNU.
https://www.gnu.org/software/sed/manual/ -
POSIX.1-2024, especificación de
sed: Comportamiento portable entre implementaciones.
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/sed.html -
The Grymoire, Sed Tutorial, de Bruce Barnett: Tutorial clásico con ejemplos detallados de sustitución y operaciones multilínea.
https://www.grymoire.com/Unix/Sed.html -
CIS Benchmarks: Las secciones de remediación de cada control incluyen las órdenes de referencia para su aplicación.
https://www.cisecurity.org/cis-benchmarks -
Páginas de manual del sistema:
man sed,man sshd_configyman login.defs.
9. Licencia
Licencia CC BY-NC-ND 4.0
Esta página forma parte del curso Bastionado de Redes y Sistemas © 2026 de José Juan Sánchez Hernández y su contenido se distribuye bajo una licencia Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International.
Esta licencia exige que quienes reutilicen el material otorguen el debido crédito al autor. Permite copiar y redistribuir el material en cualquier medio o formato, únicamente en su forma original, y solo para fines no comerciales.