Minimal SSH server that securely delivers a secret to authenticated clients.
Connect, authenticate, receive your secret, done.
Warning
This software has not been independently tested, audited, or reviewed for security. Use it at your own risk and do not rely on it in security-critical environments.
| Platform | Status |
|---|---|
| Linux | Supported |
- Flexible authentication: public key, password, or both
- Optional username check: restrict connections to a specific SSH username
- Three secret sources: inline value, file on disk, or environment variable — each optionally encrypted (client provides passphrase)
- Concurrent connections: thread-per-connection model, no queueing
- Auth timeout: configurable timeout for the authentication phase (default 30 s)
- Startup validation: port range, key files, and secret source are checked before binding
- Configurable logging: four levels (
debug,info,warn,error) with optional file output - Systemd service: ships with a hardened unit file and a daily-restart timer
cmake -B build -G "Ninja Multi-Config" .cmake --build build --config ReleaseThe Ninja Multi-Config
generator lets you use other build types (
CMAKE_BUILD_TYPE)
like Debug or RelWithDebInfo, apart from Release.
The resulting binaries are in build/<build-type>/.
A config file is mandatory. ssh-drop reads it from config/ssh-drop.conf by default, or from a path passed as the
first argument:
./ssh-drop /etc/ssh-drop/ssh-drop.confThese must always be present in the config file:
| Key | Description |
|---|---|
port |
TCP port to listen on (1–65535) |
host_key |
Path to the server's private host key |
auth_method |
Authentication mode: publickey, password, or both |
A secret source is also required — exactly one of:
| Key | Description |
|---|---|
secret |
Inline secret value |
secret_file |
Path to a file whose contents are the secret |
secret_env |
Name of an environment variable holding the secret |
| Key | Required when | Description |
|---|---|---|
authorized_keys |
auth_method is publickey or both |
Path to the authorized public keys file |
A password source is required when auth_method is password or both — exactly one of:
| Key | Description |
|---|---|
auth_password |
Inline password value |
auth_password_file |
Path to a file whose contents are the password |
auth_password_env |
Name of an environment variable holding the password |
| Key | Default | Description |
|---|---|---|
auth_timeout |
30 |
Seconds before an unauthenticated connection is dropped |
log_level |
info |
Minimum log level: debug, info, warn, error |
log_file |
(empty) | Path to a log file (see below) |
secret_encrypted |
false |
Set to true if the secret is encrypted (see below) |
When log_file is omitted, errors go to stderr and everything else to stdout.
When log_file is set, output goes to both the console (as above) and the file.
Clients authenticate with a key listed in the authorized_keys file (auth_method = publickey).
Set auth_method = password and provide exactly one password source:
| Key | Description |
|---|---|
auth_password |
Inline password value |
auth_password_file |
Path to a file whose contents are the password |
auth_password_env |
Name of an environment variable holding the password |
Set auth_method = both to require both a valid public key and a correct password. The server enforces a strict
pubkey-first order: only the public key method is advertised initially. The password prompt is only revealed after the
key is verified via SSH partial authentication. This prevents attackers from brute-forcing passwords without a valid
key.
To restrict which SSH username is accepted, set exactly one:
| Key | Description |
|---|---|
auth_user |
Inline username value |
auth_user_file |
Path to a file whose contents are the username |
auth_user_env |
Name of an environment variable holding the username |
When set, the connecting client's SSH username must match. When omitted, any username is accepted.
secret_file, auth_password_file, and auth_user_file read the entire file contents, including any trailing
newline. Use echo -n or printf when writing these files to avoid an unintended trailing newline:
printf 'my-secret-value' > /etc/ssh-drop/secretWhen secret_encrypted = true, the secret source (whichever of secret, secret_file, or secret_env is used) is
expected to contain base64-encoded encrypted data. The client must send the decryption passphrase as the first line of
input after connecting. The passphrase never touches disk — it exists only in memory for the instant needed to decrypt,
then is discarded.
Encryption scheme: PBKDF2-SHA256 (210,000 iterations) derives a 256-bit key, which is used with AES-256-GCM for authenticated encryption. The output is base64-encoded text, so it works with all three secret sources.
ssh-drop --encrypt secret/secret.encYou will be prompted for a passphrase (twice for confirmation) and the secret value. The output is a base64 text file.
ssh-drop --decrypt secret/secret.encYou will be prompted for the passphrase. On success the decrypted secret is printed to stdout; on failure (wrong passphrase, missing file) an error is printed to stderr and the exit code is 1.
secret_file = secret/secret.enc
secret_encrypted = trueecho "my-passphrase" | ssh user@host -p 7022The client pipes the passphrase into the SSH session. The server reads it, decrypts the secret, writes the plaintext back, and closes the connection.
Public key only:
port = 7022
host_key = /etc/ssh-drop/id_ed25519
authorized_keys = /etc/ssh-drop/authorized_keys
auth_method = publickey
secret_file = /etc/ssh-drop/secretPassword only:
port = 7022
host_key = /etc/ssh-drop/id_ed25519
auth_method = password
auth_password_file = /etc/ssh-drop/password
secret_file = /etc/ssh-drop/secretBoth (multi-factor):
port = 7022
host_key = /etc/ssh-drop/id_ed25519
authorized_keys = /etc/ssh-drop/authorized_keys
auth_method = both
auth_password_env = SSH_DROP_PASSWORD
auth_user = deploy
secret_file = /etc/ssh-drop/secretEncrypted secret (public key + client passphrase):
port = 7022
host_key = /etc/ssh-drop/id_ed25519
authorized_keys = /etc/ssh-drop/authorized_keys
auth_method = publickey
secret_file = /etc/ssh-drop/secret.enc
secret_encrypted = truessh-keygen -t ed25519 -f key/id_ed25519 -N ""Append each client's public key to the authorized keys file:
cat ~/.ssh/id_ed25519.pub >> key/authorized_keysPick one approach — for example, write a secret to a file:
echo "my-secret-value" > secret/secretOr set it inline in the config:
secret = my-secret-valueA config file must exist at config/ssh-drop.conf, or pass a custom path as the first argument:
./ssh-drop
./ssh-drop /etc/ssh-drop/ssh-drop.confPublic key mode:
ssh localhost -p 7022Password mode:
ssh localhost -p 7022 -o PreferredAuthentications=passwordEncrypted secret mode (pipe the decryption passphrase):
echo "my-passphrase" | ssh user@host -p 7022On successful authentication the secret is printed and the connection closes.
sudo useradd -r -s /usr/sbin/nologin ssh-drop
sudo install -Dm755 build/Release/ssh-drop /usr/local/bin/ssh-drop
sudo install -Dm640 config/ssh-drop.conf /etc/ssh-drop/ssh-drop.conf
sudo install -Dm600 key/id_ed25519 /etc/ssh-drop/id_ed25519
sudo install -Dm644 key/authorized_keys /etc/ssh-drop/authorized_keys
sudo install -Dm640 secret/secret /etc/ssh-drop/secret
sudo chown -R ssh-drop:ssh-drop /etc/ssh-dropsudo install -Dm644 deploy/ssh-drop.service /etc/systemd/system/ssh-drop.service
sudo install -Dm644 deploy/ssh-drop-restart.service /etc/systemd/system/ssh-drop-restart.service
sudo install -Dm644 deploy/ssh-drop-restart.timer /etc/systemd/system/ssh-drop-restart.timersudo systemctl daemon-reload
sudo systemctl enable --now ssh-drop.service
sudo systemctl enable --now ssh-drop-restart.timersudo systemctl status ssh-drop
sudo journalctl -u ssh-drop -fLicensed under the Apache License 2.0.