> For the complete documentation index, see [llms.txt](https://docs.bluerock.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bluerock.io/bluedev-container/bluedev-identity-deployment-kit.md).

# BlueDev Identity Deployment Kit

The Bluedev Identity Deployment Kit provides instructions for bringing up a StepCA server to manage OIDC provisioners and handle enrollment requests initiated by `bluedev-bouncer` on BlueRock hosts.

The StepCA server can be deployed either directly within a BlueRock VM instance or on a separate VM to serve multiple BlueRock instances. The StepCA package contains Docker files for StepCA and Caddy, along with `just` command scripts for setup and management.

* **StepCA:** Registers OIDC provisioners for `bluedevx` sign-in and accepts enrollment authentication requests initiated by `bluedev-bouncer`.
* **Caddy (Optional):** Handles automatic TLS certificate generation and maintenance from Let's Encrypt for public deployments. For private, VPN-gated network architectures, StepCA can run independently without Caddy by binding directly to a private interface.

<figure><img src="/files/c4WgzUVkV3UZ3MG45Lza" alt=""><figcaption></figcaption></figure>

### StepCA Deployment

{% hint style="success" icon="lightbulb-exclamation-on" %}
**Important:**

Whether deployed on a BlueRock VM or an independent VM, StepCA can generate TLS certificates using a Public IP address instead of a DNS domain. For successful enrollment, ensure firewall or security group rules permit inbound traffic from BlueRock host instances to the StepCA port via this IP.
{% endhint %}

#### Prerequisites and Installation

1. Extract the package source:

   ```shellscript
   $ mkdir bluedevca
   $ cp /opt/bluerock/bluedev-identity-ca-0.1.0.tar.gz ~/bluedevca/
   $ cd ~/bluedevca/
   $ tar zxvf bluedev-identity-ca-0.1.0.tar.gz
   ```
2. Install prerequisites:
   1. For AL2023:

      ```shellscript
      $ sudo dnf install -y spal-release
      $ sudo dnf install -y just
      $ sudo dnf install docker
      $ sudo usermod -aG docker ec2-user
      $ sudo systemctl enable docker.service
      $ sudo systemctl start docker.service
      $ sudo systemctl status docker
      $ sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m) -o /usr/libexec/docker/cli-plugins/docker-compose
      $ sudo chmod +x /usr/local/bin/docker-compose
      ```
   2. For Ubuntu:

      ```shellscript
      $ sudo apt-get update
      $ sudo apt-get install -y docker.io just curl
      $ sudo usermod -aG docker $USER
      $ sudo systemctl enable docker.service
      $ sudo systemctl start docker.service
      $ sudo systemctl status docker
      $ sudo mkdir -p /usr/lib/docker/cli-plugins
      $ sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m) -o /usr/lib/docker/cli-plugins/docker-compose
      $ sudo chmod +x /usr/lib/docker/cli-plugins/docker-compose
      ```

### Configure Environment

Navigate to the `compose` directory and configure the environment settings:

```shellscript
$ cd compose/
$ cp .env.example .env
$ vi .env
```

Set the bouncer password matched during BlueRock Host enrollment:

```shellscript
BOUNCER_PASSWORD=<BOUNCER_PASSWORD>
```

### **Choose a Deployment Architecture**

Configure the `.env` file based on the required network architecture:

#### Private Deployment (without Caddy)&#x20;

Use this configuration for a CA reachable over a VPN-gated private subnet or SSH tunnel (without Caddy). StepCA publishes directly on a specific interface using self-signed TLS certificates generated for the designated private IP.

```shellscript
# Host address the :9000 listener publishes on
STEPCA_BIND_ADDR=<PRIVATE_IP_ADDRESS>

# Host port for the publish + client URL
STEPCA_BIND_PORT=9000
```

{% hint style="info" icon="notes-sticky" %}
**Note:**

Avoid setting `STEPCA_BIND_ADDR` to `0.0.0.0`. This configuration generates the leaf certificate without Subject Alternative Name (SAN) information and bypasses strict firewall routing. Specify the exact private IP address and let the subnet's security group serve as the security boundary.

For without caddy deployment, `CADDY_DOMAIN` should be blank.
{% endhint %}

#### Public Deployment (with Caddy)

Use this configuration for a publicly reachable StepCA server using Let's Encrypt or a local CA:

```shellscript
# Public DNS address OR Public IP address of the Step CA VM instance
CADDY_DOMAIN=<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>

# Email used for Let's Encrypt registration
CADDY_ACME_EMAIL=<ADMIN_EMAIL>

# Standard incoming ports to allow in AWS Security Group
CADDY_HTTP_PORT=80
CADDY_HTTPS_PORT=443
```

{% hint style="info" icon="notes-sticky" %}
**Note:**

When a registered public DNS domain is unavailable, set `CADDY_DOMAIN` directly to the Public IP address of the VM instance (for example, `CADDY_DOMAIN=000.0.000.00`). StepCA will include the Public IP in the Subject Alternative Names (SANs) of the certificate so client handshakes validate without certificate naming errors.

For without caddy deployment, `CADDY_DOMAIN` should be blank.
{% endhint %}

### Configure Google Identity Provisioner

1. Create a Google OIDC Desktop Client App via the Google Cloud Console (*Requires admin access privileges*).

{% hint style="info" icon="notes-sticky" %}
**Note:**

Set the `redirect_uri` to `https://127.0.0.1/`
{% endhint %}

2. Copy and edit the Google provider template:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ cd providers/
   $ cp provisioner.env.example google.env
   $ vi google.env
   </code></pre>
3. Configure the required parameters:

   ```shellscript
   PROVISIONER_NAME=google
   ISSUER_URL=https://accounts.google.com
   CLIENT_ID=<GOOGLE_CLIENT_ID>
   CLIENT_SECRET=<GOOGLE_CLIENT_SECRET>
   ADMINS=<ADMIN_EMAIL_ADDRESS>
   DOMAIN_RESTRICT=<AUTH_DOMAIN>
   BLUEDEV_DOMAINS=<AUTH_DOMAIN>
   DEFAULT_SSH_CERT_DURATION=8h
   MAX_SSH_CERT_DURATION=24h
   ```

**Google Workspace Provisioner Configuration Variables**

Review and define the following variables carefully before executing the provisioner setup script. Incorrect values will prevent successful host enrollment and developer authentication.

| **Variable**                | **Description**                                                                      | **Notes**                                                                               |
| --------------------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `PROVISIONER_NAME`          | The internal identifier for the OIDC provisioner within the StepCA configuration.    | Example: `google`                                                                       |
| `ISSUER_URL`                | The OpenID Connect discovery endpoint for Google Workspace.                          | The value must be exactly `[https://accounts.google.com](https://accounts.google.com)`. |
| `CLIENT_ID`                 | The OAuth 2.0 Client ID generated within the Google Cloud Console credentials page.  | Required to identify the application to Google's authorization servers.                 |
| `CLIENT_SECRET`             | The sensitive OAuth 2.0 Client Secret paired with the designated Client ID.          | Must be kept secure and never committed to version control.                             |
| `ADMINS`                    | A comma-separated list of administrator email addresses authorized to manage the CA. | Example: `admin@domain.com`                                                             |
| `DOMAIN_RESTRICT`           | The specific email domain authorized to authenticate via this OIDC provisioner.      | Example: `bluerock.io`                                                                  |
| `BLUEDEV_DOMAINS`           | The domain name utilized for generating the SSH principal mapping.                   | Typically matches the `DOMAIN_RESTRICT` value.                                          |
| `DEFAULT_SSH_CERT_DURATION` | The standard time-to-live (TTL) validity period assigned to issued SSH certificates. | Example: `8h`                                                                           |
| `MAX_SSH_CERT_DURATION`     | The absolute maximum validity period permitted for issued SSH certificates.          | Example: `24h`                                                                          |

#### Service Bring-Up and Management

Review available commands using `just help`:

```shellscript
bluedev-identity — CA-side deployment kit

  SETUP
    just up [--no-caddy]              bring step-ca [+ caddy] up; register provisioners
    just add <name>                   register OIDC provisioner from providers/<name>.env
    just remove <name>                remove OIDC provisioner from step-ca

  SHARE WITH DOWNSTREAM
    just fingerprint [<h:p>|--auto]   print CA root SPKI (or full pin URL)
    just bouncer-info                 print bouncer enrollment inputs (URL + name + password)

  MAINTENANCE
    just update [ca|caddy|all]        apply config changes (default: all) — non-destructive
    just rotate-bouncer-password      re-key bouncer provisioner (DESTRUCTIVE: invalidates enrolled bouncers)
    just info                         read-only health + state probe
    just dump-config [ca|caddy|defaults]
                                      print live container config (default: ca)
    just down [--force]               destructive teardown (containers + volumes)

  KIT DEVELOPMENT
    just test [unit|component|all]    run the test gate (auto-detects step-ca for component)

  Per-verb detail:  just <verb> --help
```

1. Register the OIDC provider and start services:

   ```shellscript
   $ just add google

   $ just up
   ```
2. Check health status:

   ```shellscript
   $ just info
   ```

   \
   Expected Output:

   ```shellscript
   == bluedev-identity info (read-only) ==

     filesystem
       [ ok ]   compose/.env present                     
       [ ok ]   BOUNCER_PASSWORD set                     

     docker
       [ ok ]   daemon reachable                         
       [ ok ]   compose plugin v2                        

     containers
       [ ok ]   step-ca running                          
       [ ok ]   caddy running                            (ec2-3-144-79-172.us-east-2.compute.amazonaws.com)

     step-ca api
       [ ok ]   /health 200 ok                           

     provisioners
       [ ok ]   google registered                        

     bluedev JWK provisioners
       [ ok ]   bluedev-bouncer registered               (dev-host bouncers auth as this)
       [ ok ]   admin (break-glass)                      info-only; not for routine signing

   Summary: HEALTHY

   ```
3. Retrieve the auto-generated CA enrollment string:

   ```shellscript
   $ just fingerprint --auto
   ```

   Output format:

   ```shellscript
   https://<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>:443#fp=<CA_FINGERPRINT_HASH>

   ## Example using Public IP:
   `https://000.0.000.00:443#fp=a1b2c3d4e5...`
   ```

   \
   For private deployments, the generated fingerprint URL utilizes the specified private IP address.\
   \
   Output format:

   ```shellscript
   https://<STEP_CA_VM_PRIVATE_IP>:443#fp=<CA_FINGERPRINT_HASH>

   ## Example using Private IP:
   `https://x0x.x0.x0.xx:443#fp=a1b2c3d4e5...`
   ```

{% hint style="info" %}
**Note:**

To redeploy StepCA after configuration modifications, execute the teardown command followed by the bring-up command matching the chosen deployment architecture:

```shellscript
$ just down --force
$ just up
```

{% endhint %}

#### Enroll BlueRock Host

Log in to the BlueRock VM and execute the enrollment command using the generated CA URL:

```shellscript
$ sudo bluedev-bouncer enroll --ca https://<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>:443#fp=<CA_FINGERPRINT_HASH> --passoword
```

Enter the `BOUNCER_PASSWORD` when prompted

{% hint style="info" icon="notes-sticky" %}
**Note:**

`<STEP_CA_VM_PUBLIC_DNS_OR_PUBLIC_IP>` accepts either the domain name or raw **Public IP address** of your StepCA server.
{% endhint %}

### Configure Microsoft Entra ID Provisioner

1. Create an OIDC Desktop App using App Registrations in the Azure Portal.

{% hint style="info" icon="notes-sticky" %}
**Note:**

Set the `redirect_uri` to `https://localhost/` (`127.0.0.1` is not supported for Entra ID).
{% endhint %}

2. Create and edit `entra.env`:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ cd providers/
   $ vi entra.env
   </code></pre>
3. Configure the required parameters:

   ```shellscript
   PROVISIONER_NAME=entra
   ISSUER_URL=https://login.microsoftonline.com/<TENANT_ID>/v2.0/.well-known/openid-configuration
   CLIENT_ID=<ENTRA_OIDC_APP_CLIENT_ID>
   ADMINS=<ADMIN_EMAIL_ADDRESS>
   DOMAIN_RESTRICT=<AUTH_DOMAIN>
   BLUEDEV_DOMAINS=<AUTH_DOMAIN>
   DEFAULT_SSH_CERT_DURATION=8h
   MAX_SSH_CERT_DURATION=24h
   ```

**Entra ID Provisioner Configuration Variables**

Review and define the following variables carefully before executing the provisioner setup script. Incorrect values will prevent successful host enrollment and developer authentication.

| Variable                    | Description                                                                          | Notes                                                 |
| --------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| `PROVISIONER_NAME`          | The internal identifier for the OIDC provisioner within the StepCA configuration.    | Example: `entra`                                      |
| `ISSUER_URL`                | The OpenID Connect discovery endpoint for the Microsoft Entra ID tenant.             | Replace `<TENANT_ID>` with the exact Entra tenant ID. |
| `CLIENT_ID`                 | The Application (client) ID generated during the Entra ID app registration process.  | Located in the Entra ID application overview panel.   |
| `ADMINS`                    | A comma-separated list of administrator email addresses authorized to manage the CA. | Example: `admin@domain.com`                           |
| `DOMAIN_RESTRICT`           | The specific email domain authorized to authenticate via this OIDC provisioner.      | Example: `bluerock.io`                                |
| `BLUEDEV_DOMAINS`           | The domain name utilized for generating the SSH principal mapping.                   | Typically matches the `DOMAIN_RESTRICT` value.        |
| `DEFAULT_SSH_CERT_DURATION` | The standard time-to-live (TTL) validity period assigned to issued SSH certificates. | Example: `8h`                                         |
| `MAX_SSH_CERT_DURATION`     | The absolute maximum validity period permitted for issued SSH certificates.          | Example: `24h`                                        |

#### Bring-Up Service

Register the Entra ID provider and start services:

```shellscript
$ just add entra
$ just up
```

#### Retrieve Fingerprint and Enroll Host

1. Get the host enrollment string:

   ```shellscript
   $ just fingerprint --auto
   ```

   \
   Expected output:

   ```shellscript
   https://<STEP_CA_VM_PUBLIC_DNS>:443#fp=<CA_FINGERPRINT_HASH>
   ```
2. Log in to the target BlueRock VM and execute enrollment:

   ```shellscript
   $ sudo bluedev-bouncer enroll --ca https://<STEP_CA_VM_PUBLIC_DNS>:443#fp=<CA_FINGERPRINT_HASH> --password
   ```

   \
   Enter the `BOUNCER_PASSWORD` when prompted

### Unenroll a BlueRock Host

If the BlueRock Identity Service was previously configured on a host instance, the bouncer must be unenrolled to prevent configuration conflicts before switching to a different authentication method (such as SSH Key Trust) or decommissioning the environment.

To reverse a host enrollment and clear the Certificate Authority (CA) trust configuration, execute the following command directly on the BlueRock host instance terminal:

```shellscript
$ sudo bluedev-bouncer unenroll
```

Command Actions:

The `unenroll` command reverses the `enroll` process. Upon execution, it performs the following system modifications:

* Removes the bouncer SSH configuration file (`/etc/ssh/sshd_config.d/51-bluedev-bouncer-ca.conf`).
* Deletes the CA public key located at `/etc/bluedev/user_ca.pub` (unless the `--keep-ca-pubkey` flag is specified).
* Removes all host-certificate files (unless the `--keep-host-cert` flag is specified).

### Bluedev Bouncer CLI Reference

The `bluedev-bouncer` runs directly on the target BlueRock host to handle system setup and CA trust enrollment. Below is the complete command-line interface surface for operator reference.

```shellscript
$ sudo bluedev-bouncer --help
```

Expected output:

```shellscript
CLI surface of the bouncer.

The binary has exactly two shapes:

- **Session mode** — sshd `ForceCommand` runs the binary with empty argv; clap parses no subcommand; the bouncer reads the verb from `SSH_ORIGINAL_COMMAND` and routes through the wire verb allowlist (`crates/verbs`). The wire cannot reach any operator surface. - **Operator mode** — one of the [`Command`] subcommands. All host-mutation and diagnostic verbs go here for a single uniform surface. Plan 08 (`plans/08-install-subcommand.md`) drove the lifecycle half; `self-check` / `print-paths` / `build-catalog` / `install-catalog-packages` were promoted alongside so the operator-facing CLI doesn't mix flag-style and subcommand-style modes.

Usage: bluedev-bouncer [OPTIONS] [COMMAND]

Commands:
  install                   Install bluedev-bouncer on this host. Performs the host setup: useradd, conffile placement, state-dir creation, sshd snippet two-phase commit, tmpfiles, systemctl reload. Idempotent; requires root
  uninstall                 Uninstall bluedev-bouncer. Reverses `install`. Catalog and packages dirs are preserved unless `--purge`
  enroll                    Opt-in CA trust + (optional) host-cert signing via a CA provisioner. Writes `/etc/bluedev/user_ca.pub`, optionally signs the host's SSH host keys, writes `/etc/ssh/sshd_config.d/51-bluedev-bouncer-ca.conf`. Trust-only path and JWK signing path are both wired
  unenroll                  Reverse of `enroll`. Removes the `51-*` snippet, the CA pubkey at `/etc/bluedev/user_ca.pub` (unless `--keep-ca-pubkey`), and every host-cert file (unless `--keep-host-cert`)
  doctor                    Read-only health probe. Reports install + enroll status, host-cert expiry warnings (<7 days = warn; expired = error), `sshd -t` cleanliness, allowlist shape validity. Exit 0 = clean; 1 = warnings; 2 = errors
  self-check                Operator health probe. Verifies docker daemon reachability, devcontainer CLI on PATH, `git >= 2.25`, allowlist files present, scratch perms, etc. Exit codes per check
  print-paths               Print resolved `[paths]` values from the config file as shell-eval-safe `KEY='value'` lines on stdout. One parser (this binary) owns the TOML schema; downstream scripts read from here
  build-catalog             Pre-build every devcontainer template under `[paths] catalog_root` so subsequent `up` invocations skip the slow image-build step. Writes to `bluedev-primed/<id>:latest`
  install-catalog-packages  Bake every template's `[paths] packages_root` `.deb`/`.rpm` contents into the primed image. Skips `devcontainer build` — `--install-catalog-packages` invokes it internally — so subsequent `up` skips both build AND runtime package install
  link                      Serve one protocol-v2 link connection over stdin/stdout. The v2 sshd `ForceCommand` entry point: reads the handshake from stdin, authenticates from the SSH session environment, and runs the multiplexed dispatch loop until the link closes
  help                      Print this message or the help of the given subcommand(s)

Options:
      --config <CONFIG>
          Path to the bouncer's main config file. Applies to every subcommand that loads config (session mode included)
          
          [default: /etc/bluedev/bouncer.toml]

  -h, --help
          Print help (see a summary with '-h')

```

### View Active `bluedev-bouncer` Sessions

Execute the `bluedev-bouncer sessions` command to display all containers managed by the `bluedev-bouncer`   and view active client sessions on the BlueRock host:

```bash
$ sudo bluedev-bouncer sessions

CONTAINER ID  NAME                   WORKSPACE                 OWNER                      IMAGE                                      STATE    SESSIONS  CLIENTS
9e224510d573  coral-claude-ar-c1     claude-workload-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/coral-claudedesktop:latest  running  1         <CLIENT_IP> (11m)
5f19294b67f5  ubuntu-bluerock        ubuntu-bluerock-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/ubuntu-bluerock:latest      running  1         <CLIENT_IP> (15s)
8c998a803c05  ubuntu-bluerock-ar-c2  ubuntu-bluerock-<ID>      <USER_EMAIL>@<domain>.com  bluedev-primed/ubuntu-bluerock:latest      running  2         <CLIENT_IP> (10m), <CLIENT_IP> (3m)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.bluerock.io/bluedev-container/bluedev-identity-deployment-kit.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
