This project is a centralized authentication and secrets provisioning server for Bootstrap (Not to be confused the with CSS framework). It is designed to allow arbitrary client machines to securely request access and retrieve sensitive environment variables or secrets during their initialization phase. It relies on strict cryptographic verification using Ed25519 asymmetric keys to ensure that only explicitly authorized clients receive secrets.
By utilizing modern encryption protocols, this system provides a highly secure mechanism to bootstrap new servers, workstations, or deployment targets without ever transmitting plain text secrets over the network or storing them insecurely.
The system uses a completely zero trust model for secret delivery. The server holds a master key that encrypts all secrets at rest within a local SQLite database using the AES 256 GCM authenticated encryption algorithm.
When a new client machine requests access to the secrets, it generates a local Ed25519 key pair and submits its public key to the server. To prevent unauthorized access, an administrator or an already approved device must cryptographically sign this new public key to authorize the new device.
Once the device is approved, the new client must prove it actually possesses the private key it claimed to own by signing a random challenge nonce generated by the server. Only after this verification succeeds does the server retrieve the encrypted secrets from the database. The server decrypts them in memory, and then immediately encrypts them again. This second encryption is performed using the age encryption protocol, specifically targeting the approved client public key.
This strict protocol guarantees that even if the network transport is entirely compromised, the payload remains mathematically inaccessible to anyone except the exact client device that generated the initial request.
sequenceDiagram
autonumber
actor Admin
participant Requester as Requester Client (b me)
participant Server as Auth Server
participant Approver as Approver Client (b trust)
Requester->>Server: POST /api/register (Host, OS, Public Key)
Server-->>Requester: 200 OK (user_code, challenge_nonce)
Note over Requester: Displays user_code & begins polling...
Admin->>Approver: Exec: b trust <user_code>
Approver->>Server: GET /api/pending/<user_code>
Server-->>Approver: 200 OK (Requester Public Key)
Note over Approver: Admin confirms public key fingerprint
Approver->>Server: POST /api/approve (user_code, Admin Signature & Fingerprint)
Note over Server: Server verifies signature against admin list
Requester->>Server: POST /api/challenge/poll (user_code, Signature of Nonce)
Server-->>Requester: 200 OK (Encrypted Secrets Payload)
Note over Requester: Decrypts payload using local Ed25519 private key
To compile and install the server directly from the source code, you will need the Rust toolchain installed on your system.
Execute the following command in your terminal to build the release binary:
cargo build --releaseThe compiled executable will be located in the target release directory. You can copy this binary to any location in your execution path.
Alternatively, you can utilize the included Docker configuration to build a container image without needing the Rust compiler installed locally:
docker build -t bootstrap_auth_server:latest .The server requires minimal setup. It compiles to a single binary and utilizes SQLite for its persistent storage, eliminating the need for complex database infrastructure.
The application is configured using standard environment variables:
SERVER_MASTER_KEY: A highly secure string used to derive the AES encryption key. This is required for encrypting and decrypting the database secrets.
Note
You can easily generate a cryptographically secure string directly in your terminal using the following utility:
openssl rand -base64 32DATABASE_URL: The connection string for the SQLite database. Defaults to a local file named data.db.- SERVER_PORT : The network port the Axum server will listen on. Defaults to 3000.
Before starting the application, you must define the required environment variables. Create a file named secrets.json in the same directory where you plan to execute the server if you wish to provision initial data.
Start the server by passing the master key as an environment variable:
SERVER_MASTER_KEY="your_highly_secure_random_string_here" cargo run --releaseIf you are using Docker, you can start the container in detached mode and map the necessary ports and volumes:
docker run -d \
-p 3000:3000 \
-e SERVER_MASTER_KEY="your_highly_secure_random_string_here" \
-v /absolute/path/to/data:/app/data \
bootstrap_auth_server:latestOnce the server is running, you can monitor the logs to verify that the database migrations have completed successfully and that the server is actively listening for incoming authentication requests on the specified port.
When the server starts, it automatically looks for a file named secrets.json in the current working directory. If this file is found, the server parses the key and value pairs, encrypts the values securely using the master key, and stores them persistently in the database.
After successful ingestion, the server renames the file to secrets.json.bak. This automatic cleanup prevents accidental plain text exposure and ensures the secrets are not ingested multiple times.
The server exposes three primary endpoints for the device registration lifecycle.
Clients send their system hostname, operating system details, and their Ed25519 public key in standard Secure Shell format to the registration endpoint. The server registers the device in a pending state and generates a short user code along with a secure challenge nonce.
Request Payload: A JSON object containing:
hostname(string)os(string)public_key(string)
Response Payload: A JSON object containing:
user_code(string)challenge_nonce(string)expires_in(integer representing seconds)
An administrator submits the user code along with a cryptographic signature. This signature must be the pending device public key signed by the administrator private key. The server verifies this signature against known approved devices to authorize the new client.
Request Payload: A JSON object containing:
user_code(string)approver_public_key_fingerprint(string)signature(base64 encoded string)
Response Payload: A JSON string confirming approval.
The pending client polls the server with their user code and a signature of the challenge nonce. This proves the client actually possesses the private key corresponding to their submitted public key. If the device has been approved, the server returns the secrets encrypted via the age protocol for that specific client. If the device is still pending, the server responds with an appropriate status code indicating the client should continue waiting.
Request Payload: A JSON object containing:
user_code(string)signature(base64 encoded string of the challenge nonce signature)
Response Payload: A JSON object containing:
encrypted_secrets(base64 encoded string containing the age encrypted payload)
- David Wong for his brief intro to EdDSA and Ed25519 on his blog post.