--- title: "Cómo instalar HashiCorp Boundary en Ubuntu 24.04" description: "Instala HashiCorp Boundary en Ubuntu 24.04 con PostgreSQL, controller y worker en systemd, TLS de Let's Encrypt y un target SSH para acceder sin VPN." category: "Herramientas de Seguridad" language: es source: https://cubepath.com/es/docs/herramientas-de-seguridad/instalacion-de-boundary-acceso-remoto-seguro updated: 2026-09-25 --- # Cómo instalar HashiCorp Boundary en Ubuntu 24.04 HashiCorp Boundary da acceso a servidores internos por sesiones autenticadas y auditables, sin repartir acceso a toda la red como haría una VPN. El usuario inicia sesión en el controller, pide conectarse a un target concreto y un worker abre un túnel TCP solo hacia ese host y puerto. En este tutorial instalarás un controller y un worker de Boundary en el mismo servidor Ubuntu 24.04, con PostgreSQL como base de datos, TLS de Let's Encrypt y un target SSH hacia un servidor de tu red privada. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS y al menos 2 GB de RAM, por ejemplo un VPS de CubePath, con una IP pública y acceso a la red privada donde están los servidores que quieres publicar. - Un usuario no root con privilegios `sudo`. - Un dominio con un registro A que apunte a la IP pública del servidor. En la guía se usa `boundary.your_domain`. - Un servidor interno accesible por SSH desde el servidor de Boundary. En la guía su IP privada es `10.0.0.20`. - La CLI de Boundary en tu equipo para administrar y conectarte (se explica en el paso 6). Puertos que usa esta instalación: | Puerto | Uso | Exposición | |---|---|---| | 9200/TCP | API y panel web del controller | Público | | 9201/TCP | Clúster (worker hacia controller) | Solo local en esta guía | | 9202/TCP | Proxy del worker (túneles de sesión) | Público | | 9203/TCP | Endpoint de salud (ops) | Solo local | | 80/TCP | Emisión y renovación del certificado | Público | ## Paso 1: Instalar Boundary desde el repositorio de HashiCorp Descarga la clave GPG de HashiCorp en `/etc/apt/keyrings`: ```bash sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/hashicorp-archive-keyring.gpg ``` Añade el repositorio para tu versión de Ubuntu: ```bash echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(. /etc/os-release && echo "$VERSION_CODENAME") main" | sudo tee /etc/apt/sources.list.d/hashicorp.list ``` Instala Boundary y comprueba la versión: ```bash sudo apt update sudo apt install boundary boundary version ``` ```output Version information: Build Date: 2026-... Git Revision: ... Version Number: 0.x.x ``` Asegúrate de que existe un usuario de sistema `boundary` para ejecutar los servicios (el paquete puede haberlo creado ya) y de que existe el directorio de configuración: ```bash getent passwd boundary >/dev/null || sudo useradd --system --home /etc/boundary.d --shell /usr/sbin/nologin boundary sudo install -d -o boundary -g boundary -m 0750 /etc/boundary.d /etc/boundary.d/tls ``` ## Paso 2: Preparar PostgreSQL El controller guarda en PostgreSQL toda su configuración (scopes, usuarios, targets) y el estado de las sesiones. Instala PostgreSQL 16, el de Ubuntu 24.04: ```bash sudo apt install postgresql ``` Crea un rol y una base de datos propiedad de ese rol. Sustituye `your_db_password` por una contraseña larga y aleatoria: ```bash sudo -u postgres psql -c "CREATE ROLE boundary WITH LOGIN PASSWORD 'your_db_password';" sudo -u postgres psql -c "CREATE DATABASE boundary OWNER boundary;" ``` Comprueba que el rol puede conectarse: ```bash psql "postgresql://boundary:your_db_password@127.0.0.1:5432/boundary" -c 'SELECT current_user;' ``` ```output current_user -------------- boundary (1 row) ``` ## Paso 3: Obtener el certificado TLS El controller sirve su API y el panel web por HTTPS. Instala Certbot y abre los puertos necesarios en UFW: ```bash sudo apt install certbot sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 9200/tcp sudo ufw allow 9202/tcp sudo ufw enable ``` Boundary se ejecuta como el usuario `boundary`, que no puede leer `/etc/letsencrypt`. Crea un hook que copie el certificado a `/etc/boundary.d/tls` cada vez que se emita o renueve y reinicie el controller si está en marcha: ```bash sudo nano /etc/letsencrypt/renewal-hooks/deploy/boundary.sh ``` ```bash #!/usr/bin/env bash set -euo pipefail install -o boundary -g boundary -m 0644 "${RENEWED_LINEAGE}/fullchain.pem" /etc/boundary.d/tls/cert.pem install -o boundary -g boundary -m 0600 "${RENEWED_LINEAGE}/privkey.pem" /etc/boundary.d/tls/key.pem if systemctl is-active --quiet boundary-controller.service; then systemctl restart boundary-controller.service fi ``` Hazlo ejecutable y solicita el certificado. Certbot ejecuta los scripts de `renewal-hooks/deploy` en cada renovación, pero no en la primera emisión, así que lanzarás el hook a mano una vez: ```bash sudo chmod 0755 /etc/letsencrypt/renewal-hooks/deploy/boundary.sh sudo certbot certonly --standalone -d boundary.your_domain --agree-tos -m admin@your_domain --no-eff-email sudo RENEWED_LINEAGE=/etc/letsencrypt/live/boundary.your_domain /etc/letsencrypt/renewal-hooks/deploy/boundary.sh sudo ls -l /etc/boundary.d/tls ``` ```output -rw-r--r-- 1 boundary boundary 2868 Sep 25 11:02 cert.pem -rw------- 1 boundary boundary 241 Sep 25 11:02 key.pem ``` ## Paso 4: Configurar el controller Boundary cifra los datos sensibles de la base de datos con claves KMS. En producción conviene un KMS externo (Vault Transit, AWS KMS, etc.); en esta guía se usan claves AEAD estáticas guardadas en el archivo de configuración. Genera tres claves aleatorias de 32 bytes, una para cada propósito (`root`, `worker-auth` y `recovery`): ```bash for purpose in root worker-auth recovery; do echo "$purpose: $(openssl rand -base64 32)"; done ``` Crea el archivo del controller: ```bash sudo nano /etc/boundary.d/controller.hcl ``` Pega esta configuración, sustituyendo la contraseña de la base de datos, el dominio y las tres claves: ```hcl disable_mlock = true controller { name = "controller-1" description = "Boundary controller" database { url = "postgresql://boundary:your_db_password@127.0.0.1:5432/boundary?sslmode=disable" } } listener "tcp" { address = "0.0.0.0:9200" purpose = "api" tls_cert_file = "/etc/boundary.d/tls/cert.pem" tls_key_file = "/etc/boundary.d/tls/key.pem" } listener "tcp" { address = "127.0.0.1:9201" purpose = "cluster" } listener "tcp" { address = "127.0.0.1:9203" purpose = "ops" tls_disable = true } kms "aead" { purpose = "root" aead_type = "aes-gcm" key = "your_root_key" key_id = "global_root" } kms "aead" { purpose = "worker-auth" aead_type = "aes-gcm" key = "your_worker_auth_key" key_id = "global_worker-auth" } kms "aead" { purpose = "recovery" aead_type = "aes-gcm" key = "your_recovery_key" key_id = "global_recovery" } ``` El listener `cluster` escucha solo en `127.0.0.1` porque el worker está en la misma máquina. Si más adelante añades workers en otras redes, cámbialo a una IP accesible por ellos y abre el puerto 9201 solo para sus direcciones. Este archivo contiene la contraseña de la base de datos y las claves, así que restringe sus permisos: ```bash sudo chown boundary:boundary /etc/boundary.d/controller.hcl sudo chmod 0640 /etc/boundary.d/controller.hcl ``` Inicializa la base de datos. Este comando se ejecuta una sola vez, crea el esquema y genera un método de autenticación por contraseña con un usuario `admin`, una organización, un proyecto y un target de ejemplo: ```bash sudo -u boundary boundary database init -config /etc/boundary.d/controller.hcl ``` ```output Initial auth information: Auth Method ID: ampw_1234567890 Auth Method Name: Generated global scope initial password auth method Login Name: admin Password: AbCdEf1234567890 Scope ID: global User ID: u_1234567890 User Name: admin ... Initial org scope information: Scope ID: o_1234567890 ... Initial project scope information: Scope ID: p_1234567890 ``` Guarda en un gestor de contraseñas el `Auth Method ID`, la contraseña de `admin` y los identificadores de la organización y el proyecto: no se vuelven a mostrar. Crea la unidad de systemd del controller: ```bash sudo nano /etc/systemd/system/boundary-controller.service ``` ```ini [Unit] Description=HashiCorp Boundary controller Wants=network-online.target After=network-online.target postgresql.service [Service] User=boundary Group=boundary ExecStart=/usr/bin/boundary server -config=/etc/boundary.d/controller.hcl Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target ``` Arranca el controller y comprueba el endpoint de salud: ```bash sudo systemctl daemon-reload sudo systemctl enable --now boundary-controller curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9203/health ``` ```output 200 ``` ## Paso 5: Configurar el worker El worker es el proceso por el que pasa el tráfico de las sesiones. Se autentica ante el controller con la misma clave `worker-auth`, y su `public_addr` es la dirección a la que se conectarán los clientes. Crea su configuración: ```bash sudo nano /etc/boundary.d/worker.hcl ``` ```hcl disable_mlock = true listener "tcp" { address = "0.0.0.0:9202" purpose = "proxy" } worker { name = "worker-1" description = "Worker con acceso a la red privada" public_addr = "boundary.your_domain" initial_upstreams = ["127.0.0.1:9201"] } kms "aead" { purpose = "worker-auth" aead_type = "aes-gcm" key = "your_worker_auth_key" key_id = "global_worker-auth" } ``` Restringe sus permisos y crea su unidad de systemd: ```bash sudo chown boundary:boundary /etc/boundary.d/worker.hcl sudo chmod 0640 /etc/boundary.d/worker.hcl sudo nano /etc/systemd/system/boundary-worker.service ``` ```ini [Unit] Description=HashiCorp Boundary worker Wants=network-online.target After=network-online.target boundary-controller.service [Service] User=boundary Group=boundary ExecStart=/usr/bin/boundary server -config=/etc/boundary.d/worker.hcl Restart=on-failure RestartSec=5 LimitNOFILE=65536 [Install] WantedBy=multi-user.target ``` Arranca el worker y comprueba en el log que se ha conectado al controller: ```bash sudo systemctl daemon-reload sudo systemctl enable --now boundary-worker sudo journalctl -u boundary-worker -n 30 --no-pager ``` Si ves errores de autenticación, la clave `worker-auth` no coincide exactamente con la del controller. ## Paso 6: Instalar la CLI en tu equipo e iniciar sesión La administración y las conexiones se hacen desde tu equipo. En Ubuntu o Debian, añade el mismo repositorio del paso 1 e instala `boundary`; en macOS usa `brew install hashicorp/tap/boundary`. Para que las sesiones SSH funcionen, el equipo también necesita el cliente `ssh`. Apunta la CLI a tu controller e inicia sesión con el `Auth Method ID` del paso 4: ```bash export BOUNDARY_ADDR=https://boundary.your_domain:9200 boundary authenticate password -auth-method-id=ampw_1234567890 -login-name=admin ``` La CLI pide la contraseña y guarda el token en el llavero del sistema: ```output Authentication information: Account ID: acctpw_1234567890 Auth Method ID: ampw_1234567890 User ID: u_1234567890 The token was successfully stored in the chosen keyring and is not displayed here. ``` Comprueba que el worker está registrado: ```bash boundary workers list ``` También puedes abrir `https://boundary.your_domain:9200` en el navegador: el panel web muestra las mismas organizaciones, proyectos y sesiones. ## Paso 7: Crear un target SSH Boundary organiza los recursos en scopes: `global`, organizaciones y proyectos. Los hosts y targets viven en un proyecto; usa el que generó `database init` (`p_1234567890` en el ejemplo). Crea un catálogo de hosts estático, añade el servidor interno y agrúpalo en un host set: ```bash boundary host-catalogs create static -scope-id=p_1234567890 -name="servidores-internos" boundary hosts create static -host-catalog-id=hcst_1234567890 -name="web-01" -address="10.0.0.20" boundary host-sets create static -host-catalog-id=hcst_1234567890 -name="web" boundary host-sets add-hosts -id=hsst_1234567890 -host=hst_1234567890 ``` Cada comando devuelve el identificador del recurso creado (`hcst_...`, `hst_...`, `hsst_...`), que usas en el siguiente. Crea ahora un target TCP en el puerto 22 y asígnale el host set: ```bash boundary targets create tcp -scope-id=p_1234567890 -name="ssh-web-01" -default-port=22 boundary targets add-host-sources -id=ttcp_1234567890 -host-source=hsst_1234567890 ``` ## Paso 8: Conectarse y gestionar sesiones `boundary connect ssh` pide una sesión al controller, abre un túnel local a través del worker y lanza tu cliente `ssh` contra él: ```bash boundary connect ssh -target-id=ttcp_1234567890 -username=your_user ``` La autenticación SSH en el servidor de destino sigue siendo la tuya (clave o contraseña); Boundary controla quién puede llegar hasta él y registra la sesión. Mientras estás conectado, desde otra terminal puedes ver la sesión activa: ```bash boundary sessions list -scope-id=p_1234567890 ``` ```output Session information: ID: s_1234567890 Status: active Target ID: ttcp_1234567890 User ID: u_1234567890 ``` Un administrador puede cortar una sesión en cualquier momento: ```bash boundary sessions cancel -id=s_1234567890 ``` Una vez que el acceso por Boundary funciona, el servidor interno ya no necesita tener el puerto 22 abierto hacia Internet: basta con que sea accesible desde el worker por la red privada. ## Solución de problemas **El controller no arranca.** Revisa `sudo journalctl -u boundary-controller -n 50`. Los errores más comunes son una URL de base de datos incorrecta, permisos sobre los archivos de `/etc/boundary.d/tls` o una clave KMS que no son 32 bytes en base64. **`boundary connect` se queda esperando o falla con timeout.** El cliente debe llegar a `boundary.your_domain:9202`. Comprueba el firewall con `sudo ufw status` y que el `public_addr` del worker resuelve a la IP pública. **La sesión se abre pero SSH no conecta con el host.** El worker no alcanza el destino. Desde el servidor de Boundary, prueba `nc -zv 10.0.0.20 22`. ## Conclusión Has instalado un controller y un worker de Boundary con PostgreSQL y TLS, y has publicado un servidor interno como target SSH al que se accede con sesiones autenticadas, visibles y revocables. Como siguientes pasos puedes crear usuarios y roles limitados por proyecto en lugar de usar `admin`, conectar un proveedor OIDC para el inicio de sesión y desplegar workers adicionales dentro de otras redes privadas para alcanzar sus hosts sin exponerlos.