Skip to content

vugarfamiloglu/bastion-ssh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔐 Bastion SSH

A from-scratch SSH-2 server in TypeScript/Node. It implements the SSH transport, authentication and connection layers by hand — version exchange, KEXINIT negotiation, curve25519 key exchange, an ed25519 host key, the AES-GCM binary packet protocol, publickey & password authentication, and session channels with an interactive shell and exec. It runs on the OpenSSH client today (ssh, scp-style exec), with zero third-party dependencies.

TypeScript Node.js 22.6+ License Apache 2.0 Zero dependencies From scratch

The crypto principle: the SSH protocol (binary packets, key-exchange orchestration, channel multiplexing, auth) is written from scratch — but the cryptographic primitives (X25519, Ed25519, AES-GCM, SHA-256, scrypt) come from Node's vetted built-in crypto. Never roll your own AES.


Verified against OpenSSH

$ ssh -i key -p 2222 admin@127.0.0.1 whoami
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: aes128-gcm@openssh.com
debug1: SSH2_MSG_NEWKEYS received
Authenticated to 127.0.0.1 using "publickey".
admin (role: admin)

Interactive sessions (ssh admin@host) get a jailed sandbox shell; ssh host <cmd> runs a single command and returns its output and exit status.


Features

Layer What's implemented
Transport (RFC 4253) Version exchange, KEXINIT negotiation, curve25519-sha256 KEX, ed25519 host key + signature, key derivation, NEWKEYS, AES-256/128-GCM packet protocol
Authentication (RFC 4252) publickey (ed25519 signature verification against authorized keys) and password (scrypt), banner, lockout
Connection (RFC 4254) session channel, pty-req, shell, exec, env, data, exit-status, clean close
Shell sandboxed command console (help, whoami, sysinfo, ls, cd, cat, …) confined by a path jail
Security modern algorithms only (no sha1/cbc/3des), per-user authorized keys, path-traversal jail, no real shell by default

🛠 Tech Stack

Language Runtime Crypto Storage Protocol

Layer Technology
Language TypeScript — run directly on Node (native type stripping), no build step
Runtime Node.js 22.6+ (net, crypto)
Crypto Node crypto: X25519, Ed25519, AES-256/128-GCM, SHA-256, scrypt
Storage virtual users + authorized keys as JSON
Protocol SSH-2 (RFC 4253 / 4252 / 4254) hand-written

Zero dependencies — no npm install. Everything is Node's standard library.


Quickstart

Requires Node.js 22.6+ (runs .ts files directly; Node 24 recommended).

git clone https://github.com/vugarfamiloglu/bastion-ssh
cd bastion-ssh

node src/main.ts seed     # create the admin user + host key
node src/main.ts serve    # listen on :2222

Authorize your public key, then connect:

node src/main.ts addkey admin "$(cat ~/.ssh/id_ed25519.pub)"
ssh -p 2222 admin@127.0.0.1            # interactive sandbox shell
ssh -p 2222 admin@127.0.0.1 whoami     # run a single command

Password login also works:

ssh -p 2222 -o PreferredAuthentications=password admin@127.0.0.1   # Bastion2026!

CLI

node src/main.ts serve                     # run the SSH server
node src/main.ts seed                       # create admin + host key
node src/main.ts passwd <user> <password>   # set a password
node src/main.ts addkey <user> "<pubkey>"   # authorize a public key
node src/main.ts version

Configuration (BASTION_*)

Variable Default Meaning
BASTION_PORT 2222 listen port (standard SSH is 22)
BASTION_BIND 0.0.0.0 bind address
BASTION_DATA_DIR data host key, users.json, home trees
BASTION_BANNER pre-auth banner
BASTION_MAX_ATTEMPTS 6 auth attempts before disconnect

Security notes

  • Primitives from the library, protocol from scratch — see the principle above. Only modern algorithms are offered (curve25519, ed25519, AES-GCM); legacy sha1/cbc/3des/arcfour are not supported.
  • Host key — an ed25519 key is generated on first boot and stored 0600; its fingerprint is printed at startup.
  • Path-traversal jail — the sandbox shell's file commands are confined to the user's home (.. cannot escape). Covered by unit tests.
  • No real shell by default — the session surface is a sandboxed command console, not a system shell. A real PTY shell is a future opt-in.

Project layout

src/
  main.ts         CLI entrypoint (serve / seed / passwd / addkey)
  config.ts       BASTION_* configuration
  log.ts          structured logging
  wire.ts         SSH wire encoding (byte/uint32/string/mpint/name-list)
  constants.ts    message numbers + offered algorithms
  hostkey.ts      ed25519 host key (generate / load / sign)
  kex.ts          curve25519 ECDH, exchange hash, key derivation
  io.ts           pull-based byte reader over the socket
  packet.ts       binary packet protocol (none + AES-GCM modes)
  transport.ts    version exchange, KEXINIT, key exchange, NEWKEYS
  store.ts        virtual users (scrypt) + authorized keys
  auth.ts         userauth: publickey (sig verify) + password
  vfs.ts          path-traversal jail
  connection.ts   channels, pty-req, shell/exec
  session.ts      per-connection orchestration
tests/            wire / kex / vfs unit tests

Roadmap

  • P1 — transport ✅ KEX, host key, AES-GCM packet protocol, NEWKEYS.
  • P2 — authentication ✅ publickey (ed25519) + password.
  • P3 — connection ✅ session channel, shell + exec, exit status.
  • P4 — next ⏳ SFTP subsystem, chacha20-poly1305 cipher, port forwarding, rekeying, RSA keys, a web admin panel.

Testing

npm test

Unit tests cover the SSH wire encoding (including mpint edge cases), the curve25519 ECDH agreement / exchange hash / key derivation, and the path-traversal jail. The full handshake → auth → shell flow is verified against the real OpenSSH client.


License

Apache License 2.0 — see LICENSE.

About

A from-scratch SSH-2 server in TypeScript/Node — curve25519 KEX, ed25519 host key, AES-GCM transport, publickey/password auth. Zero dependencies (built-in crypto).

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages