Automatizar la creación de un dominio Samba en Ubuntu 24.04 LTS con un script
Hace unos días te explicábamos cómo Crear un controlador de dominio de Active Directory con Samba en Ubuntu 24.04 LTS. Sin embargo, soy consciente de que se trata de un trabajo algo pesado, porque son bastantes las operaciones que debemos realizar en el sistema.
Por ese motivo, se me ha ocurrido que sería buena idea automatizar toda la tarea con un script, que solo tengas que descargar, ejecutar y darle la información necesaria… Y aquí lo tienes.
En cualquier caso, si eres de los que quieren saber qué pasa bajo el capó, te explicaremos cómo funciona el script. Si no, puedes limitarte a descargarlo y darle permisos de ejecución. Así es que, si estás listo, comencemos.
Crear el script
Lo primero será crear una carpeta donde guardar el archivo con el script. Yo suelo crear una carpeta en en directorio raíz, para que esté accesible, luego comenzaré por ejecutar lo siguiente:
sudo mkdir /scripts
A continuación, lo convertimos en nuestro directorio actual:
cd /scripts
Y por último, abrimos el editor de textos, con el nombre que vamos a dar al script:
sudo nano ad-dc.sh
Contenido del script
Una vez abierto el editor de textos, debemos comenzar a escribir el script. Nosotros aquí iremos dividiéndolo en secciones, para que se comprenda más fácilmente.
-
Control de errores:
#!/bin/bash # set -euo pipefail error() { echo "ERROR: $*" >&2; exit 1; }
Con set -euo pipefail trata de que el script sea robusto frente a incidencias. -e indica que debe terminar ante cualquier comando que devuelva error, con -u terminará si se usan variables sin definir y -o pipefail hará que termine si falla cualquiera cualquiera de los comandos en una secuencia conectada por tuberías (|).
La función error() sirve para mostrar un mensaje de error personalizado y finalizar el script con un código de salida distinto de cero
-
Entrada de parámetros:
read -p "Hostname (solo el host, sin dominio, ej. dc01): " DOMAIN_HOSTNAME [[ -z "$DOMAIN_HOSTNAME" ]] && error "El hostname no puede estar vacío." read -p "Dominio DNS completo (FQDN) (ej. ejemplo.local): " DNS_DOMAIN [[ -z "$DNS_DOMAIN" ]] && error "El dominio DNS no puede estar vacío." # Sugerir NetBIOS (parte antes del punto, en mayúsculas) SUGGESTED_NETBIOS_DOMAIN=$(echo "$DNS_DOMAIN" | cut -d'.' -f1 | tr '[:lower:]' '[:upper:]') read -p "Dominio NetBIOS [${SUGGESTED_NETBIOS_DOMAIN}]: " NETBIOS_DOMAIN NETBIOS_DOMAIN=${NETBIOS_DOMAIN:-$SUGGESTED_NETBIOS_DOMAIN} read -p "Dirección IP con CIDR (ej. 192.168.1.10/24): " SERVER_IP_CIDR [[ -z "$SERVER_IP_CIDR" ]] && error "La IP no puede estar vacía." SERVER_IP=${SERVER_IP_CIDR%/*} read -p "Gateway predeterminado: " GATEWAY [[ -z "$GATEWAY" ]] && error "El gateway no puede estar vacío." read -p "Servidores DNS forwarder (espacio‑separados) [8.8.8.8 1.1.1.1]: " DNS_FORWARDERS DNS_FORWARDERS=${DNS_FORWARDERS:-"8.8.8.8 1.1.1.1"}
La orden read, con el argumento -p lee una entrada del usuario, ofreciéndole un mensaje informativo. En nuestro caso, lo usamos para pedir el nombre del equipo (hostname), los nombres DNS y NetBIOS del dominio, su dirección IP, el Gateway y los servidores DNS externos.
Después de cada entrada, comprobamos si la variable obtenida está vacía y, en caso afirmativo, producimos un error. Por ejemplo, aquí te incluyo la que hace referencia al nombre del equipo.
[[ -z "$DOMAIN_HOSTNAME" ]] && error "El hostname no puede estar vacío."
Algo similar hacemos con el nombre DNS del dominio, con la dirección IP y con el Gateway.
En el caso del nombre NetBIOS, primero le damos un valor predeterminado. El comando cut extrae la primera etiqueta del dominio y tr la convierte en mayúsculas:
SUGGESTED_NETBIOS_DOMAIN=$(echo "$DNS_DOMAIN" | cut -d'.' -f1 | tr '[:lower:]' '[:upper:]')
Después, si el valor ofrecido por el usuario está vacío, asignamos el valor predeterminado:
NETBIOS_DOMAIN=${NETBIOS_DOMAIN:-$SUGGESTED_NETBIOS_DOMAIN}
En cuanto a la contraseña, usamos el argumento -s para ocultarla mientras la escribe el usuario y un bucle para que se solicite mientras esté vacía o tenga menos de 8 caracteres
while true; do read -s -p "Contraseña para 'Administrator': " ADMIN_PASSWORD echo [[ -z "$ADMIN_PASSWORD" ]] && { echo "La contraseña no puede estar vacía."; continue; } [[ ${#ADMIN_PASSWORD} -lt 8 ]] && { echo "La contraseña debe tener al menos 8 caracteres."; continue; } break done
Por último, el reino Kerberos lo inicializamos con el nombre DNS del dominio:
KERBEROS_REALM=$(echo "$DNS_DOMAIN" | tr '[:lower:]' '[:upper:]')
-
Validar la entrada:
Una vez inicializadas todas las variables, mostramos al usuario sus valores, para que se asegure de que son correctos:
echo "" echo "--- Resumen ---" echo " Hostname: $DOMAIN_HOSTNAME" echo " Dominio DNS: $DNS_DOMAIN" echo " Dominio NetBIOS: $NETBIOS_DOMAIN" echo " IP / CIDR: $SERVER_IP_CIDR" echo " Gateway: $GATEWAY" echo " DNS Forwarders: $DNS_FORWARDERS" echo "------------------" read -p "¿Continuar? (s/N): " CONFIRM [[ "$CONFIRM" != "s" && "$CONFIRM" != "S" ]] && { echo "Abortado."; exit 0; }
Como ves en la última línea, si la respuesta no es afirmativa, se interrumpe el proceso.
-
Preparar el sistema para la instalación:
En el siguiente bloque de instrucciones, preparamos el sistema para la provisión del dominio.
Lo primero será evitar que, durante la instalación o actualización de paquetes, se muestren preguntas al usuario. En su lugar, se usarán las respuestas predeterminadas. Lo conseguiremos definiendo la variable de entorno DEBIAN_FRONTEND con el valor noninteractive:
export DEBIAN_FRONTEND=noninteractive
Después, actualizamos el sistema e instalamos los paquetes necesarios para la provisión del dominio:
apt-get update apt-get dist-upgrade -y apt-get install -y samba krb5-user winbind dnsutils chrony acl attr ufw
Los paquetes que instalamos son:
-
samba, el núcleo de Active Directory
-
krb5-user, que será el encargado de la autenticación
-
winbind, para la resolución de IDs)
-
chrony, que será el responsable de la sincronización horaria mediante NTP
-
dnsutils, que incluye herramientas de consulta y diagnóstico de problemas en DNS,
-
acl, para gestionar listas de control de acceso
-
attr, para gestionar atributos extendidos en archivos y directorios
-
ufw, que es el cortafuegos
Deshabilitamos los servicios smbd y nmbd, porque vamos a sustituir su funcionalidad con samba-ad-dc:
systemctl disable --now smbd nmbd || true
Configuramos el nombre del equipo, primero con el comando hostnamectl y, a continuación, añadiéndolo al archivo /etc/hosts:
hostnamectl set-hostname "$DOMAIN_HOSTNAME" sed -i "/127.0.0.1.*$DOMAIN_HOSTNAME/d" /etc/hosts echo "127.0.0.1 $DOMAIN_HOSTNAME.$DNS_DOMAIN $DOMAIN_HOSTNAME localhost" >> /etc/hosts
-
-
Establecer una dirección estática:
Como sabemos, un controlador de dominio debe tener una dirección IP estática. Es lo que hacemos en el siguiente bloque de instrucciones.
Lo primero será determinar la interfaz con la ruta predeterminada. comenzamos usando ip -o -4 route show to default para obtener la ruta predeterminada IPv4 de la tabla de enrutamiento del sistema. A continuación, usamos una tubería para enviar la salida al comando awk ‘{print $5}’, que extrae el quinto campo de la línea (el nombre de la interfaz de red), y lo almacenamos en la variable INTERFACE.
INTERFACE=$(ip -o -4 route show to default | awk '{print $5}')
Si, después de esto, la variable está vacía, mostramos un error:
[[ -z "$INTERFACE" ]] && error "No se pudo detectar la interfaz de red."
Lo siguiente será encontrar el primer archivo de configuración de Netplan (que tendrá como extensión .yaml) dentro de /etc/netplan. Para lograrlo usaremos el comando find, para localizar archivos (-type f) con la extensión correcta (-name ‘*.yaml’) dentro de la ruta mencionada, pero sin entrar en subdirectorios (-maxdepth 1).
Una vez que obtengamos la lista, usaremos una tubería (|) para ordenarla (con sort) y obtener solo el primer elemento (head -n 1)… Y lo guardamos en la variable NETPLAN_FILE.
Si, después de todo, la variable está vacía, mostramos un error:
NETPLAN_FILE=$(find /etc/netplan -maxdepth 1 -type f -name '*.yaml' | sort | head -n 1) [[ -z "$NETPLAN_FILE" ]] && error "No se encontró ningún archivo .yaml en /etc/netplan."
Llegados a este punto, estamos listos para modificar el archivo de configuración, pero antes, hacemos una copia de respaldo, con el mismo nombre, pero añadiéndole el texto ‘.bak.‘, la fecha y hora de la copia:
cp "$NETPLAN_FILE" "${NETPLAN_FILE}.bak.$(date +%F_%T)"
Comenzaremos preparando la lista de servidores DNS para Netplan. Estos valores deben estar en la variable DNS_FORWARDERS, pero los encontraremos separados por espacios, mientas que Netplan los necesita separados por comas. Afortunadamente, para hacer el cambio solo tenemos que obtenerlos con echo y enviarlos al comando sed para que busque espacios y los sustituya por comas seguidas de espacios. El resultado lo guardaremos en la variable
DNS_FORWARDERS_NETPLAN:DNS_FORWARDERS_NETPLAN=$(echo "$DNS_FORWARDERS" | sed 's/ /, /g')
A continuación, usamos el comando cat para sobrescribir el archivo de configuración de Netplan (recuerda que su nombre está en la variable NETPLAN_FILE). Y para establecer el nuevo contenido usaremos <<EOF que envía al comando cat todo el contenido que encuentre hasta el siguiente EOF.
Para generar el contenido, iremos incluyendo las variables que hemos configurado más arriba. En definitiva, algo como esto:
cat > "$NETPLAN_FILE" <<EOF network: version: 2 renderer: networkd ethernets: $INTERFACE: dhcp4: no addresses: - $SERVER_IP_CIDR routes: - to: default via: $GATEWAY nameservers: addresses: [127.0.0.1, $DNS_FORWARDERS_NETPLAN] EOF
Ya solo nos quedará aplicar los cambios y dar unos segundos a los dispositivos para que habiliten los nuevos valores:
netplan apply sleep 5
-
Habilitar la sincronización horaria:
Active Directory necesita que todos los ordenadores implicados estén sincronizados temporalmente, admitiendo desviaciones de tiempo bastante pequeñas. Por ese motivo, es importante que tengamos habilitada la sincronización horaria a través de un servidor NTP.
Para lograrlo, usaremos el comando systemctl indicándole que el servicio chrony (que mantiene la hora sincronizada) se inicie en cada arranque (enable) y que también se active en este momento (–now):
systemctl enable --now chrony
-
Provisionar el dominio Samba:
Llegados a este punto, el sistema estará listo para comenzar a provisionar el dominio Samba, pero antes detendremos el servicio, para evitar conflictos inesperados.
Como antes, usaremos el comando systemctl, pero ahora indicando que pare (stop) el servicio samba-ad-dc.
systemctl stop samba-ad-dc || true
Hemos añadido || true para evitar que el script muestre un error y se detenga, cuando systemctl falle (por ejemplo, cuando el servicio ya esté parado o no exista).
A continuación, usamos el comando rm para eliminar, de forma recursiva y forzada (-rf), todo el contenido de las carpetas /var/lib/samba y /var/cache/samba, y el archivo /etc/samba/smb.conf. De este modo, eliminamos toda la configuración y los datos previos de Samba:
rm -rf /etc/samba/smb.conf /var/lib/samba/* /var/cache/samba/*
Y para completar esta parte, realizamos la provisión propiamente dicha, con los datos que recabamos al principio:
samba-tool domain provision \ --use-rfc2307 \ --realm="$KERBEROS_REALM" \ --domain="$NETBIOS_DOMAIN" \ --server-role=dc \ --dns-backend=SAMBA_INTERNAL \ --adminpass="$ADMIN_PASSWORD" \ --host-name="$DOMAIN_HOSTNAME" \ --host-ip="$SERVER_IP" \ --option="interfaces=lo $INTERFACE" \ --option="bind interfaces only=yes" \ --option="dns forwarder = $(echo $DNS_FORWARDERS | awk '{print $1}')"
-
Realizar ajustes pos-instalación:
Una vez completada la provisión, deberemos sustituir el archivo /etc/resolv.conf (el archivo donde los programas de GNU/Linux consultan los servidores DNS), con un enlace simbólico que apunte a la configuración DNS gestionada por systemd-resolved, que se encuentra en /run/systemd/resolve/stub-resolv.conf. Esto asegura que el sistema utilice la nueva configuración de forma centralizada y actualizada
Para lograrlo usamos el comando ln con el argumento -sf (para crear el enlace simbólico de forma forzada, sobrescribiendo el archivo de destino en caso de que exista). A continuación, incluimos el archivo al que apuntamos y la ubicación del enlace simbólico:
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
Completaremos la tarea habilitando de nuevo los servicios implicados:
systemctl unmask samba-ad-dc systemctl enable --now samba-ad-dc systemctl enable --now winbind
-
Configurar el cortafuegos:
La última etapa de este proceso consiste en ajustar el funcionamiento del cortafuegos, para garantizar el acceso a los servicios necesarios.
GNU/Linux utiliza de forma predererminada el cortafuegos UFW (Uncomplicated Firewall), de hecho, lo instalamos al principio para asegurarnos de que estaría disponible. Ahora ha llegado el momento de habilitarlo:
ufw --force enable
Usamos –force para que se ejecute sin pedir confirmación. De este modo, no se detiene la ejecución del script.
A continuación, definimos una lista con los puertos y protocolos que necesitaremos habilitar para los servicios DNS, Kerberos, NTP, LDAP, SMB, etc., que son necesarios para el controlador de dominio Samba (algo como esto: «53» «88» «123/udp» «135/tcp» «137/udp» «138/udp» «139/tcp» «389» «445/tcp» «464» «636/tcp» «3268/tcp» «3269/tcp»). Y crearemos un bucle que recorra la lista, habilitándolos uno a uno.
for RULE in \ "53" "88" "123/udp" "135/tcp" "137/udp" "138/udp" "139/tcp" \ "389" "445/tcp" "464" "636/tcp" "3268/tcp" "3269/tcp" ; do ufw allow $RULE done
También habilitamos las conexiones TCP a los puertos dinámicos. Son los comprendidos en el rango 49152 a 65535 y los suelen usar las aplicaciones para conexiones temporales:
ufw allow 49152:65535/tcp
Finalmente, reiniciamos el cortafuegos:
ufw reload
-
Comprobar el funcionamiento:
Por último, realizaremos una pequeña comprobación, para asegurarnos de que el controlador responde de forma adecuada. Le pediremos una lista de usuarios y, si responde correctamente, aparecerá un mensaje confirmándolo:
echo "Comprobando que Samba responde..." samba-tool user list && echo "✓ Dominio provisionado con éxito."
Como ves, hemos utilizado el comando samba-tool para consultar la lista de usuarios. Si lo combinamos con el operador && para mostrar un mensaje, el mensaje solo aparece cuando el comando samba-tool devuelve una respuesta.
Si quieres evitar la molestia de escribir el script, o copiarlo, lo tienes listo para descargar aquí.
Ya tenemos el script, ¿y ahora qué?
Bueno, pues ahora viene la parte divertida. Vamos a sacarle partido a nuestro trabajo. Pero antes, debemos dar permisos de ejecución al script. Lo haremos con una orden como esta:
sudo chmod +x ad-dc.sh
Ten en cuenta que el directorio actual debe ser el mismo donde has guardado el script (… o añadir la ruta al nombre). Si lo deseas, también puedes modificar el nombre del script, para que se ajuste a tus preferencias.
A partir de aquí, ya podemos agitar la barita mágica:
sudo ./ad-dc.sh
No olvides usar sudo y poner ./ delante del nombre del script, para hacer referencia a la ruta actual. Como antes, si el script se encuentra en otro sitio, solo hay que ajustar el valor de la ruta.
A partir de aquí, deberemos responder con la información correspondiente a la implementación del controlador de dominio que queremos configurar: Nombre del controlador, nombre del dominio (tanto DNS como NetBIOS), su dirección IP estática, la dirección de la puerta de enlace, los servidores DNS y, por último, la contraseña que tendrá la cuenta Administrador del dominio.
Después de esto, el script nos muestra los datos de partida (los mismos que acabamos de introducir, para que comprobemos que son correctos).
A partir de aquí, el script se pondrá a trabajar para nosotros. Casi cuando esté acabando, nos mostrará un resumen de la implementación
En realidad, después de esto, sólo quedará ajustar las reglas del cortafuegos y hacer la verificación final.
Para lograrlo, basta con usar una instrucción como esta:
sudo reboot
… aunque, como recordarás de otros artículos, Ubuntu dispone de varias formas para obtener el mismo resultado.
Y con esto, hemos completado la tarea que nos encomendamos al principio de este artículo. Espero que te haya resultado útil.