Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installation

This chapter walks through the complete installation procedure for deploying Vouch in an air-gapped environment, from downloading packages on a connected machine through enrolling users on the isolated network.

Step 1: Download Packages for Offline Transfer

On a connected machine, download the required packages from packages.vouch.sh:

# Import Vouch GPG signing key
curl -fsSL https://packages.vouch.sh/gpg/vouch.asc | gpg --import

# Download server RPM
curl -LO https://packages.vouch.sh/rpm/x86_64/vouch-server-<version>-1.x86_64.rpm

# Download CLI RPM (for each workstation architecture)
curl -LO https://packages.vouch.sh/rpm/x86_64/vouch-<version>-1.x86_64.rpm
curl -LO https://packages.vouch.sh/rpm/aarch64/vouch-<version>-1.aarch64.rpm

# For Debian/Ubuntu workstations
curl -LO https://packages.vouch.sh/apt/vouch_<version>_amd64.deb
curl -LO https://packages.vouch.sh/apt/vouch_<version>_arm64.deb

For container-based or Kubernetes deployments, also download:

# Pull and save container image
docker pull ghcr.io/vouch-sh/vouch:<version>
docker save ghcr.io/vouch-sh/vouch:<version> -o vouch-server-<version>.tar

# Download Helm chart (for Kubernetes)
helm pull oci://ghcr.io/vouch-sh/charts/vouch-server --version 0.1.0

Generate checksums for verification after transfer:

sha256sum vouch-server-*.rpm vouch-*.rpm vouch-*.deb vouch-server-*.tar > SHA256SUMS
gpg --detach-sign SHA256SUMS

Transfer all files to the air-gapped environment via approved media.

Step 2: Verify Package Integrity

On the air-gapped network:

# Import Vouch GPG signing key (transferred separately, verified out-of-band)
gpg --import vouch-release-key.pub

# Verify checksums
gpg --verify SHA256SUMS.sig SHA256SUMS
sha256sum -c SHA256SUMS

# Verify RPM signatures
rpm -K vouch-server-<version>-1.x86_64.rpm
rpm -K vouch-<version>-1.x86_64.rpm

Step 3: Install Packages

RPM-based installation (recommended for bare metal/VM):

# Install server
rpm -ivh vouch-server-<version>-1.x86_64.rpm

# Install CLI on workstations
rpm -ivh vouch-<version>-1.x86_64.rpm

DEB-based installation:

# Install CLI on Debian/Ubuntu workstations
dpkg -i vouch_<version>_amd64.deb

Container-based installation:

# Load container image into local Docker registry
docker load < vouch-server-<version>.tar

# Verify image loaded
docker images | grep vouch

Step 4: Secure Key Generation

Generate every key on a trusted, air-gapped workstation, following your organization’s key ceremony procedures. See the Key Ceremony chapter for per-key instructions.

Key Overview

KeyTypeFormatRequiredPurpose
JWT SecretSymmetricUTF-8 (32+ chars)YesSigns internal state tokens (authorization codes, WebAuthn state, CSRF)
SSH CA KeyEd25519Base64-encoded OpenSSH PEMOptionalSign SSH certificates
OIDC Signing KeyP-256 ECDSABase64-encoded PKCS#8 PEMOptional*Sign OIDC ID tokens
TLS CertificateRSA/ECBase64-encoded PEMOptionalHTTPS encryption
TLS Private KeyRSA/ECBase64-encoded PEMOptionalHTTPS encryption

*When unset, the server generates an ephemeral key at startup; issued tokens then fail verification after a restart.

Base64-encode all PEM keys and certificates passed via environment variables — multi-line PEM content does not survive as an environment-variable value.

Step 5: Database Setup

Vouch defaults to SQLite, which backs a single node. The database is created automatically on first startup.

# SQLite (default, single-node)
export VOUCH_DATABASE_URL="sqlite:/data/vouch.db?mode=rwc"

# Create the data directory, readable only by the service user
mkdir -p /data
chmod 700 /data

For multi-node deployments, use PostgreSQL on the internal network:

# PostgreSQL (multi-node, must be reachable on the internal network)
export VOUCH_DATABASE_URL="postgres://user:password@db.internal:5432/vouch"

Database migrations run automatically on server startup.

Step 6: Configure Vouch Server

Vouch is configured entirely through environment variables. Create a secure environment file:

# Create environment file (chmod 600 after editing)
cat > /etc/vouch/vouch.env << 'EOF'
# =============================================================================
# Vouch Server Configuration - Air-Gapped Environment
# =============================================================================

# -----------------------------------------------------------------------------
# Required Configuration
# -----------------------------------------------------------------------------

# JWT signing secret (minimum 32 characters)
VOUCH_JWT_SECRET=<your-64-character-secret-here>

# Relying Party configuration
VOUCH_RP_ID=auth.internal
VOUCH_RP_NAME=Vouch (Air-Gapped)

# Database
VOUCH_DATABASE_URL=sqlite:/data/vouch.db?mode=rwc

# -----------------------------------------------------------------------------
# Network Configuration
#
# Development (no TLS):
#   Server listens on VOUCH_LISTEN_ADDR (default: 0.0.0.0:3000)
#
# Production (TLS enabled):
#   Server automatically listens on port 443 (HTTPS) and port 80 (HTTP redirect)
#   VOUCH_LISTEN_ADDR is ignored when TLS is configured
#   HTTP requests on port 80 are 308 redirected to HTTPS on port 443
#   The /health endpoint is accessible on HTTP (for load balancer health checks)
#   Host header is validated against rp_id to prevent injection attacks
#   Requires CAP_NET_BIND_SERVICE capability (handled by packaging scripts)
# -----------------------------------------------------------------------------

# Listen address (used only when TLS is NOT configured)
VOUCH_LISTEN_ADDR=0.0.0.0:3000

# Base URL (how clients reach the server)
VOUCH_BASE_URL=https://auth.internal

# -----------------------------------------------------------------------------
# TLS Configuration (base64-encoded PEM)
# Generate with: base64 -i cert.pem | tr -d '\n'
# -----------------------------------------------------------------------------

VOUCH_TLS_CERT=<base64-encoded-certificate>
VOUCH_TLS_KEY=<base64-encoded-private-key>

# -----------------------------------------------------------------------------
# SSH CA Configuration (base64-encoded PEM)
# Generate with: base64 -i ssh_ca_key | tr -d '\n'
# -----------------------------------------------------------------------------

# SSH CA private key (base64-encoded PEM, takes precedence over path)
VOUCH_SSH_CA_KEY=<base64-encoded-ssh-ca-private-key>

# Or use a file path instead (file contains raw PEM, not base64):
# VOUCH_SSH_CA_KEY_PATH=/secrets/ssh_ca_key

# -----------------------------------------------------------------------------
# OIDC Provider Configuration
# Generate with: base64 -i oidc_signing_key.pem | tr -d '\n'
# -----------------------------------------------------------------------------

# Vouch acts as an OIDC provider - this key signs the ID tokens (base64-encoded PEM)
VOUCH_OIDC_SIGNING_KEY=<base64-encoded-oidc-signing-key>

# -----------------------------------------------------------------------------
# Upstream Identity Provider (Required)
# At least one IdP must be configured; the server refuses to start without one.
# -----------------------------------------------------------------------------

VOUCH_IDPS=internal-oidc
VOUCH_IDP_INTERNAL_OIDC_TYPE=oidc
VOUCH_IDP_INTERNAL_OIDC_ISSUER=https://idp.internal
VOUCH_IDP_INTERNAL_OIDC_CLIENT_ID=vouch-client
VOUCH_IDP_INTERNAL_OIDC_CLIENT_SECRET=<client-secret>

# -----------------------------------------------------------------------------
# Session Configuration
# -----------------------------------------------------------------------------

# Session duration (default: 8 hours)
VOUCH_SESSION_HOURS=8

# Device code settings (for CLI enrollment)
VOUCH_DEVICE_CODE_EXPIRES=600
VOUCH_DEVICE_POLL_INTERVAL=5

# -----------------------------------------------------------------------------
# Security Configuration
# -----------------------------------------------------------------------------

# Allowed email domains for enrollment (comma-separated)
VOUCH_ALLOWED_DOMAINS=internal,company.local

# DPoP (Demonstrating Proof of Possession). DPoP is always enabled and cannot
# be turned off; only the accepted proof age is configurable.
VOUCH_DPOP_MAX_AGE=300

# Extra CA bundle for outbound HTTPS, so the server trusts an internal PKI when
# fetching the IdP's discovery document or SAML metadata.
VOUCH_EXTRA_CA_CERTS=/etc/vouch/internal-ca.pem

# -----------------------------------------------------------------------------
# Audit and Retention
# -----------------------------------------------------------------------------

# Cleanup interval (minutes, 0 to disable)
VOUCH_CLEANUP_INTERVAL=15

# Event retention (days)
VOUCH_AUTH_EVENTS_RETENTION_DAYS=730
VOUCH_OAUTH_EVENTS_RETENTION_DAYS=90

# -----------------------------------------------------------------------------
# Branding (Optional)
# -----------------------------------------------------------------------------

VOUCH_ORG_NAME=Your Organization
EOF

# Secure the environment file
chmod 600 /etc/vouch/vouch.env

Environment Variables Reference

See Environment Variables for the complete list with defaults, required-ness, and validation rules. Two settings matter more in an air-gapped enclave than anywhere else:

  • VOUCH_EXTRA_CA_CERTS — a PEM bundle of extra certificate authorities for the server’s outbound HTTPS client. An internal IdP almost always presents a certificate from a private CA, and without this the discovery or metadata fetch fails at startup and the server refuses to boot.
  • VOUCH_SSH_CA_KEY_PATH — if the file at this path does not exist, the server generates a new Ed25519 CA key and writes it there. On a fresh volume that silently rotates your SSH CA and every host’s TrustedUserCAKeys entry stops matching. Provision the key before first start, or set VOUCH_SSH_CA_KEY with the PEM contents.

Step 7: Deploy Services

Deploy the server with systemd, Docker Compose, or Helm.

Option A: Systemd Service (RPM Install)

If you installed via RPM, the vouch-server systemd service is configured automatically:

# Configure environment
cp /etc/vouch/vouch.env /etc/vouch/vouch.env.local
# Edit /etc/vouch/vouch.env.local with your settings

# Start and enable the service
systemctl enable --now vouch-server

# Check status
systemctl status vouch-server

# View logs
journalctl -u vouch-server -f

Option B: Docker Compose

# docker-compose.yml
services:
  vouch-server:
    image: ghcr.io/vouch-sh/vouch:<version>
    container_name: vouch-server
    restart: unless-stopped
    ports:
      - "443:443"
    volumes:
      - vouch-data:/data
      - /etc/vouch/secrets:/secrets:ro
    env_file:
      - /etc/vouch/vouch.env
    environment:
      # Override or add environment variables here
      VOUCH_DATABASE_URL: sqlite:/data/vouch.db?mode=rwc
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "--no-check-certificate", "https://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  vouch-data:
# Start services
docker-compose up -d

# Verify container is running
docker-compose ps

# Check logs for startup errors
docker-compose logs -f vouch-server

Option C: Helm Chart (Kubernetes)

A Helm chart is available for Kubernetes deployments. After transferring the chart archive to the air-gapped environment:

# Install from the downloaded chart archive
helm install vouch-server vouch-server-0.1.0.tgz \
  --namespace vouch \
  --create-namespace \
  --set image.repository=vouch-server \
  --set image.tag=<version> \
  --values my-values.yaml

See the chart’s values.yaml for all configurable options including secrets, ingress, and persistent storage.

Verify Deployment

Regardless of deployment method:

# Verify health endpoint
curl -k https://auth.internal/health
# Expected: ok

# Verify SSH CA is loaded (if configured)
curl -k https://auth.internal/v1/credentials/ssh/ca
# Expected: {"public_key":"ssh-ed25519 AAAA...","comment":"vouch-ca@auth.internal"}

Step 8: Distribute CA Public Key

The SSH CA public key must be trusted by all SSH servers in the air-gapped environment:

The endpoint returns JSON, so extract the public_key field — writing the raw response into the file puts JSON where sshd expects a key, and every certificate login fails:

# Fetch CA public key via API
curl -sk https://auth.internal/v1/credentials/ssh/ca | jq -r .public_key > vouch-ca.pub

# Confirm it looks like a key, not JSON
cat vouch-ca.pub
# ssh-ed25519 AAAA...

# Copy to all SSH servers
scp vouch-ca.pub root@server:/etc/ssh/vouch-ca.pub

# Configure SSH server to trust the CA
echo "TrustedUserCAKeys /etc/ssh/vouch-ca.pub" >> /etc/ssh/sshd_config

# Optionally, configure authorized principals
echo "AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u" >> /etc/ssh/sshd_config

# Restart SSH daemon
systemctl restart sshd

AuthorizedPrincipals Setup (Optional but Recommended):

# Create principals directory
mkdir -p /etc/ssh/auth_principals

# For each user, create a file with allowed principals
# Vouch issues certificates with two principals: email and username
echo "john@company.internal" > /etc/ssh/auth_principals/john
echo "john" >> /etc/ssh/auth_principals/john

Step 9: Point Workstations at the Internal Server

Each workstation needs the CLI pointed at the enclave’s Vouch server, either per-invocation with --server or via the environment:

export VOUCH_SERVER=https://auth.internal

The CLI has no option for supplying an extra CA bundle. If your Vouch server presents a certificate from an internal CA, that CA must be installed in the workstation’s operating system trust store — the same requirement browsers have for the enrollment flow. Distribute it through the same channel you already use for internal PKI (MDM profile, update-ca-trust, update-ca-certificates).

This is the workstation side. For CLI configuration in general — installation, vouch enroll, and the credential helpers — see the CLI documentation at vouch.sh/docs.

Step 10: Enroll Users

Important: Enrollment requires browser access to the Vouch server’s web UI on the internal network. Users run vouch enroll on a workstation that can open the device verification URL against the internal Vouch host; there is no separate headless-only enroll mode.

Each user:

  1. Opens a browser to https://auth.internal/enroll
  2. Authenticates via the configured identity provider
  3. Registers their YubiKey through the browser’s WebAuthn prompt (touch + PIN)

After enrollment, daily login uses the CLI (vouch login) with no browser required.