Skip to content

PHD59fr/goBastion

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

72 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ goBastion

goBastion is a tool for managing SSH access, user roles, and keys on a bastion host. The project is currently under active development, and contributions are welcome!

πŸ”— GitHub Repository: https://github.com/phd59fr/goBastion

🐳 Docker Hub Image: https://hub.docker.com/r/phd59fr/gobastion


✨ Key Concept - Database as the Source of Truth

In goBastion, the database is the single source of truth for SSH keys and access management. This means that the system always reflects the state of the database. Any key or access added manually to the system without passing through the bastion will be automatically removed to maintain consistency.

How it works:

  • Key Addition: When a user adds an SSH key, it is first validated and stored in the database. The bastion then automatically synchronizes the database with the system, adding the key to the appropriate location.

  • Automatic Synchronization: The bastion enforces the database state every 5 minutes automatically. If it finds an SSH key, user, or host entry not in the database, it is immediately corrected to ensure security and consistency. The --sync flag allows triggering this on demand.

Advantages of this Approach

  • Centralized Control: All modifications go through the bastion, ensuring tight access management.
  • Enhanced Security: Unauthorized keys cannot remain on the system.
  • State Consistency: The system always mirrors the database state.
  • Audit and Traceability: Every change is recorded in the database.
  • Fully Automated Management: No need for manual checks; synchronization handles everything.
  • Easy Exportability: The system can be deployed on a new container effortlessly. Since the database is the source of truth, replicating it with synchronization scripts provides a functional bastion on a new instance.

πŸ” Features Overview

πŸ‘€ Self-Commands (Manage Your Own Account)

Command Description
πŸ”‘ selfListIngressKeys List your ingress SSH keys (keys for connecting to the bastion).
βž• selfAddIngressKey Add a new ingress SSH key (optional expiry).
❌ selfDelIngressKey Delete an ingress SSH key.
πŸ”‘ selfListEgressKeys List your egress SSH keys (keys for connecting from the bastion to servers).
πŸ”‘ selfGenerateEgressKey Generate a new egress SSH key.
πŸ“‹ selfListAccesses List your personal server accesses.
βž• selfAddAccess Add access to a personal server (supports IP restriction, TTL, protocol).
❌ selfDelAccess Remove access to a personal server.
πŸ“‹ selfListAliases List your personal SSH aliases.
βž• selfAddAlias Add a personal SSH alias.
❌ selfDelAlias Delete a personal SSH alias.
❌ selfRemoveHostFromKnownHosts Remove a host from your known_hosts file.
πŸ”„ selfReplaceKnownHost Trust a new host key after it changed (TOFU reset).
πŸ” selfSetupTOTP Enable TOTP two-factor authentication (generates QR/OTP URI).
πŸ” selfDisableTOTP Disable TOTP two-factor authentication.
πŸ”‘ selfSetPassword Set a password second factor (MFA). Required at every login if set.
πŸ”‘ selfChangePassword Change your password second factor.
πŸ”‘ selfDisablePassword Disable password second factor (MFA).
πŸ›‘οΈ selfAddIngressKeyPIV Add a PIV/YubiKey hardware-attested ingress key.
πŸ” selfGenerateBackupCodes Generate TOTP backup codes (single-use recovery codes).
πŸ” selfShowBackupCodeCount Show remaining backup codes count.

🦸 Admin Commands (Manage Other Accounts)

Command Description
πŸ“‹ accountList List all user accounts.
ℹ️ accountInfo Show detailed information about a user account.
βž• accountCreate Create a new user account.
❌ accountDelete Delete a user account.
✏️ accountModify Modify a user account (promote/demote to admin/user). Cannot demote the last remaining admin.
πŸ”‘ accountListIngressKeys List the ingress SSH keys of a user.
πŸ”‘ accountListEgressKeys List the egress SSH keys of a user.
πŸ“‹ accountListAccess List all server accesses of a user.
βž• accountAddAccess Grant a user access to a server (supports IP restriction, TTL, protocol).
❌ accountDelAccess Remove a user's access to a server.
πŸ“‹ whoHasAccessTo Show all users with access to a specific server (supports CIDR).
πŸ” accountDisableTOTP Disable TOTP two-factor authentication for a user.
πŸ”‘ accountSetPassword (admin) Set or clear a user's password second factor.
πŸ›‘οΈ pivAddTrustAnchor Register a Yubico PIV CA certificate as a trust anchor.
πŸ“‹ pivListTrustAnchors List all registered PIV trust anchor CAs.
❌ pivRemoveTrustAnchor Remove a PIV trust anchor CA.

πŸ‘₯ Group Management

Command Description
ℹ️ groupInfo Show detailed information about a group.
πŸ“‹ groupList List all groups.
βž• groupCreate Create a new group.
❌ groupDelete Delete a group.
βž• groupAddMember Add a user to a group.
❌ groupDelMember Remove a user from a group.
πŸ”‘ groupGenerateEgressKey Generate a new egress SSH key for the group.
πŸ”‘ groupListEgressKeys List all egress SSH keys associated with a group.
πŸ“‹ groupListAccesses List all accesses assigned to a group.
βž• groupAddAccess Grant access to a group (supports protocol restriction). The optional TCP connectivity check is restricted to private/reserved IP ranges to prevent network scanning. Use --force to skip.
❌ groupDelAccess Remove access from a group.
πŸ” groupSetMFA Enable or disable JIT MFA requirement for a group (owner/admin only).
βž• groupAddAlias Add a group SSH alias.
❌ groupDelAlias Delete a group SSH alias.
πŸ“‹ groupListAliases List all group SSH aliases.

πŸ” MFA / TOTP (Two-Factor Authentication)

goBastion supports multiple second-factor authentication methods that stack: password, TOTP, and JIT MFA per group.

TOTP

Command Description
selfSetupTOTP Generate a TOTP secret and display the QR/OTP URI to add to your authenticator app.
selfDisableTOTP Disable TOTP for your own account.
accountDisableTOTP (admin) Disable TOTP for any user account.

Once TOTP is enabled, the bastion will prompt for a 6-digit code at every interactive or passthrough login.

Backup Codes

Backup codes are single-use recovery codes that can be used instead of a TOTP code when you lose access to your authenticator app.

Command Description
selfGenerateBackupCodes Generate 10 new backup codes. Previous codes are invalidated.
selfShowBackupCodeCount Show how many backup codes remain unused.
  • Each code can only be used once and is removed after use.
  • Backup codes are accepted in the same prompt as TOTP codes (Enter TOTP code (or backup code):).
  • Generating new codes invalidates all previous codes.

Password Second Factor

Command Description
selfSetPassword Set a bcrypt-hashed password as a second factor. Required at every login.
selfChangePassword Change your password second factor (requires current password).
accountSetPassword (admin) Set or clear a user's password second factor.

Password MFA is independent of TOTP β€” both can be active simultaneously.

JIT MFA (per-group)

When a group has JIT MFA enabled via groupSetMFA, any user connecting via that group must pass a TOTP challenge at connection time, even if global TOTP is not enabled for their account. The user must have a TOTP secret configured (selfSetupTOTP) for this to work.

Command Description
groupSetMFA (owner/admin) Enable or disable JIT MFA for a group.

πŸ“‘ SCP / SFTP / rsync Passthrough

goBastion supports two passthrough modes depending on whether you need to use the bastion's egress key (recommended) or your own key on the target.

Mode 1 β€” sftp-session (recommended, uses bastion's egress key)

goBastion acts as a minimal SSH server and connects to the target with its own egress key. Your local key does not need to be on the target server.

Host my-server
    HostName 192.168.1.10
    User myuser
    ProxyCommand ssh -p 2222 -- bastion_user@bastion "sftp-session myuser@%h:%p"
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

StrictHostKeyChecking no is required because goBastion generates an ephemeral host key for the fake SSH server on each connection.

Then use sftp normally:

sftp my-server

Mode 2 β€” TCP proxy (requires your key on the target)

Passes -W %h:%p as a quoted string after -- so glibc does not treat it as a native SSH flag:

Host my-server
    HostName 192.168.1.10
    User myuser
    ProxyCommand ssh -p 2222 -- bastion_user@bastion "-W %h:%p"

Why -- before the hostname?
On Linux (glibc), ssh -W host:port opens a raw direct-tcpip channel that bypasses goBastion's access controls β€” and is refused by the bastion's sshd.
The -- tells SSH's option parser to stop processing flags, so -W %h:%p becomes the remote exec command forwarded to goBastion, which handles it as a controlled TCP proxy via parseTCPProxyRequest.

This enables:

  • scp file.txt user@my-server:/path/
  • sftp user@my-server
  • rsync -avz ./dir/ user@my-server:/path/

All passthrough connections are subject to the same access control rules as interactive SSH sessions.

Protocol Restriction

Access entries can be restricted to a specific transfer protocol using the --protocol flag on selfAddAccess, accountAddAccess, and groupAddAccess:

Value Meaning
ssh All protocols (default, backwards-compatible)
scpupload SCP upload only (scp -t)
scpdownload SCP download only (scp -f)
sftp SFTP only
rsync rsync only

Example: grant a user rsync-only access to a backup server:

groupAddAccess --group backups --server 10.0.0.5 --username backup --protocol rsync

⏱️ Access TTL and IP Restriction

Every access entry (selfAddAccess, accountAddAccess, groupAddAccess) supports two optional constraints:

Flag Description
--ttl <days> Access expires automatically after N days. Omit for permanent access.
--from <CIDRs> Restrict access to specific source IP ranges (comma-separated, e.g. 10.0.0.0/8,192.168.1.0/24). Omit to allow all IPs.

Both constraints are enforced at connection time - expired or out-of-range connections are denied. The Expires and From columns appear in all listAccesses outputs.

Security note (IP restrictions): If a --from CIDR restriction is set on an access entry and the bastion cannot determine the client IP (e.g. missing SSH_CLIENT), the connection is denied (fail-closed policy). This prevents accidental bypass of IP-based access controls.


πŸ›‘οΈ Yubico PIV / Hardware Key Attestation

PIV attestation lets users prove that their SSH private key was generated inside a hardware token (e.g. YubiKey) and cannot be exported. The full x509 attestation chain is verified against admin-registered CA certificates before the key is accepted.

Admin setup:

pivAddTrustAnchor --name yubico-root --cert /path/to/yubico-piv-ca.pem
pivListTrustAnchors
pivRemoveTrustAnchor --name yubico-root

User workflow (YubiKey):

# Export attestation data from YubiKey
yubico-piv-tool --action=attest --slot=9a > attest.pem
yubico-piv-tool --action=read-cert --slot=f9 > intermediate.pem
ssh-keygen -D /usr/lib/x86_64-linux-gnu/libykcs11.so -e > my_piv_key.pub

# Add the key to the bastion (chain is verified server-side)
selfAddIngressKeyPIV --attest attest.pem --intermediate intermediate.pem $(cat my_piv_key.pub)

Keys added via PIV attestation are marked PIV in selfListIngressKeys.


🐚 Mosh Support

goBastion transparently passes through mosh-server invocations, enabling Mosh sessions through the bastion. No special configuration is needed on the client side.

# Standard mosh usage - works through the bastion
mosh --ssh="ssh -J user@bastion:2222" user@my-server

The bastion detects the mosh-server command in SSH_ORIGINAL_COMMAND and exec's it directly. UDP ports 60001-61000 must be open on the target server (not the bastion) for the Mosh UDP connection.


πŸ“œ TTY Session Recording

Command Description
πŸ“‹ ttyList List recorded SSH sessions.
▢️ ttyPlay Replay a recorded SSH session.

πŸ“œ Misc Commands

Command Description
❓ help Display the help menu with available commands.
ℹ️ info Show application version and details.
πŸšͺ exit Exit the application.

πŸ“Š Permissions Matrix

πŸ” Admin Permissions

  • accountAddAccess
  • accountCreate
  • accountDelAccess
  • accountDelete
  • accountInfo
  • accountList
  • accountListAccess
  • accountListIngressKeys
  • accountListEgressKeys
  • accountModify
  • accountSetPassword
  • whoHasAccessTo
  • accountDisableTOTP
  • pivAddTrustAnchor
  • pivListTrustAnchors
  • pivRemoveTrustAnchor
  • groupCreate
  • groupDelete

Note: ttyList and ttyPlay are available to all users (for their own sessions) and to admins (for all sessions).

πŸ‘₯ Group Permissions

Permission Owner ACLKeeper GateKeeper Member
groupAddAccess βœ… βœ… βœ…
groupDelAccess βœ… βœ… βœ…
groupSetMFA βœ…
groupAddMember βœ… βœ…
groupDelMember βœ… βœ…
groupGenerateEgressKey βœ…
groupAddAlias βœ… βœ… βœ…
groupDelAlias βœ… βœ… βœ…
groupInfo βœ… βœ… βœ… βœ…
groupList βœ… βœ… βœ… βœ…
groupListAccesses βœ… βœ… βœ… βœ…
groupListAliases βœ… βœ… βœ… βœ…
groupListEgressKeys βœ… βœ… βœ… βœ…

πŸ‘€ Self Permissions

  • selfAddAccess
  • selfAddAlias
  • selfAddIngressKey
  • selfAddIngressKeyPIV
  • selfChangePassword
  • selfDelAccess
  • selfDelAlias
  • selfDelIngressKey
  • selfDisablePassword
  • selfDisableTOTP
  • selfGenerateBackupCodes
  • selfGenerateEgressKey
  • selfListAccesses
  • selfListAliases
  • selfListEgressKeys
  • selfListIngressKeys
  • selfRemoveHostFromKnownHosts
  • selfReplaceKnownHost
  • selfSetPassword
  • selfSetupTOTP
  • selfShowBackupCodeCount
  • ttyList (own sessions only)
  • ttyPlay (own sessions only)

⚠ Alias Priority Warning: If an alias is defined by the user (selfAddAlias) and the group defines an alias with the same name (groupAddAlias), the user-defined alias always takes precedence

πŸ“œ Misc Permissions

  • help
  • info
  • exit

πŸ“₯ Installation

  1. Clone the repository:

    git clone https://github.com/phd59fr/goBastion.git
    cd goBastion
  2. Build the Docker container:

    docker build -t gobastion .
  3. Run the Docker container:

    docker run --name gobastion --hostname goBastion -it -p 2222:22 gobastion:latest

    You can also use the official Docker Hub image:

    docker run --name gobastion --hostname goBastion -it -p 2222:22 phd59fr/gobastion:latest

    (optional) 3a. Launch the container with a volume to persist the database and ttyrec:

    docker run --name gobastion --hostname goBastion -it -p 2222:22 \
      -v /path/to/your/dbvolume:/var/lib/goBastion \
      -v /path/to/your/ttyvolume:/app/ttyrec gobastion:latest

    (optional) 3b. Use an external database (see Environment Variables):

    docker run --name gobastion --hostname goBastion -it -p 2222:22 \
      -e DB_DRIVER=postgres \
      -e DB_DSN="host=db user=gobastion password=secret dbname=gobastion port=5432 sslmode=disable" \
      gobastion:latest
  4. Simplified usage with an Alias (Optional):

    alias gobastion='ssh -tp 2222 user@localhost --'
  5. Connect to the bastion host (interactive mode):

    ssh -tp 2222 user@localhost (or alias gobastion)

    (optional) 5a. Connect to the bastion host with a command (non-interactive mode):

    ssh -tp 2222 user@localhost -- -osh selfListIngressKeys (or alias gobastion -osh selfListIngressKeys)

    (optional) 5b. Connect to the target host through the bastion:

    ssh -tp 2222 user@localhost -- user@targethost (ssh options supported) (or alias gobastion user@targethost)

    (optional) 5c. Use SFTP / SCP / rsync through the bastion β€” see SCP / SFTP / rsync Passthrough for full configuration details.


βš™οΈ Environment Variables

Variable Default Description
DB_DRIVER sqlite Database backend: sqlite, mysql, or postgres
DB_DSN (auto) Database connection string. For SQLite, defaults to /var/lib/goBastion/bastion.db. Required for mysql and postgres.
EGRESS_ENC_KEY (none) AES key for encrypting egress private keys at rest. See Egress Key Encryption.
LOG_FORMAT json Log output format: json (structured JSON, compatible with log aggregators) or plain (human-readable text for local debugging).

DSN examples

MySQL:

DB_DRIVER=mysql
DB_DSN=gobastion:secret@tcp(db:3306)/gobastion?charset=utf8mb4&parseTime=True&loc=Local

PostgreSQL:

DB_DRIVER=postgres
DB_DSN=host=db user=gobastion password=secret dbname=gobastion port=5432 sslmode=disable

SQLite (custom path):

DB_DRIVER=sqlite
DB_DSN=file:/data/mybastion.db?cache=shared&mode=rwc

Manual Schema Setup

If the goBastion app user does not have CREATE TABLE / ALTER permissions on your database, the DBA can pre-create the schema manually.

The sql/ directory contains ready-to-run schema files:

File Database
sql/postgres.sql PostgreSQL
sql/mysql.sql MySQL

PostgreSQL:

psql -h <host> -U <admin> -d <dbname> -f sql/postgres.sql

MySQL:

mysql -h <host> -u <admin> -p <dbname> < sql/mysql.sql

After creating the schema, grant the goBastion app user minimal privileges:

PostgreSQL:

GRANT USAGE ON SCHEMA public TO gobastion;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO gobastion;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO gobastion;

MySQL:

GRANT SELECT, INSERT, UPDATE, DELETE ON gobastion.* TO 'gobastion'@'%';
FLUSH PRIVILEGES;

The app user only needs SELECT, INSERT, UPDATE, DELETE β€” no CREATE, ALTER, or DROP.

πŸ” Egress Key Encryption

By default, egress private keys are stored in the database in plaintext. To encrypt them at rest, set the EGRESS_ENC_KEY environment variable:

# Generate a 32-byte AES-256 key
openssl rand -base64 32 > egress_key.txt
export EGRESS_ENC_KEY=$(cat egress_key.txt)

EGRESS_ENC_KEY accepts:

  • Base64-encoded AES key (16/24/32 bytes)
  • 32-byte raw passphrase

Migration behavior:

  • If EGRESS_ENC_KEY is set after keys were already stored in plaintext, existing keys are automatically re-encrypted on next use (transparent migration).
  • If EGRESS_ENC_KEY is not set, keys remain in plaintext (backward-compatible).
docker run --name gobastion --hostname goBastion -it -p 2222:22 \
  -e EGRESS_ENC_KEY="$(openssl rand -base64 32)" \
  gobastion:latest

πŸ› οΈ Admin CLI Flags

These flags are only available when running as root outside an SSH session:

Flag Command Description
--firstInstall docker exec -it goBastion /app/goBastion --firstInstall Manually bootstrap the first admin user (interactive)
--regenerateSSHHostKeys docker exec -it goBastion /app/goBastion --regenerateSSHHostKeys Force-regenerate the bastion's SSH host keys
--sync docker exec goBastion /app/goBastion --sync Enforce DB state onto the OS immediately (DB is source of truth); also runs automatically every 5 minutes
--dbExport docker exec -i -e DB_EXPORT_KEY="$DB_EXPORT_KEY" goBastion /app/goBastion --dbExport > dump Dump the database as encrypted file to stdout
--dbImport docker exec -i -e DB_EXPORT_KEY="$DB_EXPORT_KEY" goBastion /app/goBastion --dbImport < dump Restore the database from encrypted file on stdin
--disableTOTP docker exec -it goBastion /app/goBastion --disableTOTP <user> Disable TOTP, password MFA, and backup codes for a user (recovery)

πŸ” Database Export / Import

The export is now a single encrypted file, independent of SQL dialects.

It is designed for portability across:

  • SQLite
  • MySQL
  • PostgreSQL

Encryption is mandatory.


πŸ”‘ Generating and using the encryption key

openssl rand -base64 32 > export_key.txt
export DB_EXPORT_KEY=$(cat export_key.txt)

DB_EXPORT_KEY can be:

  • base64 AES key (16/24/32 bytes)
  • raw AES key
  • passphrase (derived using Argon2id)

πŸ“€ Encrypted export

docker exec -i -e DB_EXPORT_KEY="$DB_EXPORT_KEY" goBastion /app/goBastion --dbExport > dump

πŸ“₯ Encrypted import

docker exec -i -e DB_EXPORT_KEY="$DB_EXPORT_KEY" goBastion /app/goBastion --dbImport < dump

⚠️ Notes

  • Target DB must already have schema (AutoMigrate)
  • Target DB must be empty
  • Same key must be used for export/import
  • Output goes to stdout, logs to stderr

🀝 Contributing

Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. Here’s how you can help:

  • Report bugs
  • Suggest features
  • Submit pull requests

To contribute:

  1. Fork the project
  2. Create a new branch (git checkout -b feature/YourFeature)
  3. Commit your changes (git commit -m 'Add YourFeature')
  4. Push to the branch (git push origin feature/YourFeature)
  5. Open a pull request

πŸ“„ License

This project is licensed under the MIT License.


❀️ Support

A simple star on this project repo is enough to keep me motivated for days. If you’re excited about this project, let me know with a tweet. If you have any questions, feel free to reach out to me on X.


About

Tool for managing SSH access, user roles, and keys on a bastion host

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages