ssh_file(3)
πΈ NAME πΈ
ssh_file β Default callback module for the client's and server's database operations in the ssh application. And yes, this whole page is the ssh_file(3) manual, an Erlang Module Definition. Bestie, we're in it now. π«Ά
β¨ DESCRIPTION β¨
Okay so this module is literally the default callback handler for the client's and the server's user and host "database" operations. All data, for instance key pairs, are stored in files in the normal file system. This page documents the files, where they are stored and configuration options for this callback module. No cap.
The intention is to be compatible with the OpenSSH storage in files. Therefore it mimics directories and filenames of OpenSSH. It's giving "we studied the popular girl's notes and copied her whole flow." π
Ssh_file implements the ssh_server_key_api and the ssh_client_key_api. This enables the user to make an own interface using for example a database handler. Main character energy: swap in your own callback module and do your own thing.
Such another callback module could be used by setting the option key_cb when starting a client or a server (with for example ssh:connect, ssh:daemon or ssh:shell ). Yes, that space before the closing paren is in the original, keep it. π
CALLBACKS: The callback functions (host_key/2, is_auth_key/3, user_key/2, is_host_key/5, add_host_key/4) are called by the SSH application internally. They are documented here to show which files and options the default implementation uses. See ssh_server_key_api and ssh_client_key_api for the callback specifications. The public API functions are decode/2, encode/2, and extract_public_key/1. Slay.
π¦ Files, directories and who uses them π¦
Daemons
Daemons uses all files stored in the SYSDIR directory. π
Optionally, in case of publickey authorization, one or more of the remote user's public keys in the USERDIR directory are used. See the files USERDIR/authorized_keys and USERDIR/authorized_keys2.
Clients
Clients uses all files stored in the USERDIR directory. Simple as that.
Directory contents
LOCALUSERβ The user name of the OS process running the Erlang virtual machine (emulator). That's the emulator's whole identity, iykyk.SYSDIRβ This is the directory holding the server's files:ssh_host_dsa_key- private dss host key (optional)ssh_host_rsa_keyβ private rsa host key (optional)ssh_host_ecdsa_keyβ private ecdsa host key (optional)ssh_host_ed25519_keyβ private eddsa host key for curve 25519 (optional)ssh_host_ed448_keyβ private eddsa host key for curve 448 (optional)
The key files could be generated with OpenSSH's ssh-keygen command. π
At least one host key must be defined. The default value of SYSDIR is /etc/ssh.
For security reasons, this directory is normally accessible only to the root user.
lowkey important, bestie. Keep this locked down like your private story.
To change the SYSDIR, see the system_dir option.
USERDIRβ This is the directory holding the files:authorized_keysand, as second alternativeauthorized_keys2- the user's public keys are stored concatenated in one of those files.
It is composed of lines as for OpenSSH:
(options)? keytype base64-encoded-key comment
where
options :: option(,option)*
option :: % All options are skipped
keytype :: 'ssh-dsa'
| 'ssh-rsa'
| 'ssh-ecdsa-nistp256'
| 'ssh-ecdsa-nistp384'
| 'ssh-ecdsa-nistp521'
| 'ssh-ed25519'
| 'ssh-ed448'
base64-encoded-key :: % The user's public key
comment :: % Comments are skipped
known_hostsβ host keys from hosts visited concatenated. The file is created and used by the client.
It is composed of lines as for OpenSSH:
(option)? pattern(,pattern)* keytype key (comment)?
where
option :: '@revoked'
pattern :: host | '[' host ']:' port
host :: ip-address | hostname | '*'
port :: portnumber | '*'
keytype :: 'ssh-dsa'
| 'ssh-rsa'
| 'ssh-ecdsa-nistp256'
| 'ssh-ecdsa-nistp384'
| 'ssh-ecdsa-nistp521'
| 'ssh-ed25519'
| 'ssh-ed448'
key :: % encoded key from eg ssh_host_*.pub
id_dsa- private dss user key (optional)id_rsaβ private rsa user key (optional)id_ecdsaβ private ecdsa user key (optional)id_ed25519β private eddsa user key for curve 25519 (optional)id_ed448β private eddsa user key for curve 448 (optional)
The key files could be generated with OpenSSH's ssh-keygen command.
The default value of USERDIR is /home/LOCALUSER/.ssh.
To change the USERDIR, see the user_dir option
π₯ DATA TYPES π₯
experimental_openssh_key_v1_decode()β the shape is:
[{key(), openssh_key_v1_attributes()}].
experimental_openssh_key_v1_encode()β the shape is:
[{[{public_key:public_key(),
public_key:private_key(),
Comment :: binary()} |
{public_key:private_key(), Comment :: binary()}],
openssh_key_v1_attributes()}].
key()β definition:
key() :: public_key:public_key() | public_key:private_key().
The key representation. That's the vibe.
openssh_key_v1_attributes()β definition:
openssh_key_v1_attributes() :: [{atom(), term()}].
Types for the experimental implementation of the openssh_key_v1 format.
optimize_key_lookup()β definition:
optimize_key_lookup() :: {optimize, time | space}.
Make the handling of large files fast by setting time, but this will use more memory. The space variant shrinks the memory requirements, but with a higher time consumption.
To set it, set the option {key_cb, {ssh_file, [{optimize,TimeOrSpace}]} in the call of ssh:connect/3, ssh:daemon/2 or similar function call that initiates an ssh connection.
pubkey_passphrase_client_options()β definition:
pubkey_passphrase_client_options() ::
{dsa_pass_phrase, string()} |
{rsa_pass_phrase, string()} |
{ecdsa_pass_phrase, string()}.
If the user's DSA, RSA or ECDSA key is protected by a passphrase, it can be supplied with those options.
Note that EdDSA passhrases (Curves 25519 and 448) are not implemented.
system_dir_daemon_option()β definition:
system_dir_daemon_option() :: {system_dir, string()}.
Sets the system directory.
user2dir()β definition:
user2dir() ::
fun((RemoteUserName :: string()) -> UserDir :: string()).
Sets the user directory dynamically by evaluating the user2dir function.
user_dir_common_option()β definition:
user_dir_common_option() :: {user_dir, string()}.
Sets the user directory.
user_dir_fun_common_option()β definition:
user_dir_fun_common_option() :: {user_dir_fun, user2dir()}.
β¨ FUNCTIONS β¨
okay bestie, this is the part where the actual work happens π every single one of these is a little callback this module is out here serving, so let's go one by one, no skipping, no shortcuts π«Ά
add_host_key β the "new friend, noted π" one
add_host_key(Host, Port, Key, Options) -> Result
when
Host ::
inet:ip_address() |
inet:hostname() |
[inet:ip_address() | inet:hostname()],
Port :: inet:port_number(),
Key :: public_key:public_key(),
Options ::
ssh_client_key_api:client_key_cb_options(none()),
Result :: ok | {error, term()}.
This one is literally the implementation of ssh_client_key_api:add_host_key/4 β slay β¨
Option it cares about:
user_dirβ yeah that one, the only one π
File it touches:
USERDIR/known_hosts
decode β the glow-up from binary to readable π
decode(SshBin, Type) -> Decoded | {error, term()}
when
SshBin :: binary(),
Type ::
ssh2_pubkey | public_key | openssh_key |
rfc4716_key | openssh_key_v1 | known_hosts |
auth_keys,
Decoded ::
Decoded_ssh2_pubkey | Decoded_public |
Decoded_openssh | Decoded_rfc4716 |
Decoded_openssh_key_v1 | Decoded_known_hosts |
Decoded_auth_keys,
Decoded_ssh2_pubkey :: public_key:public_key(),
Decoded_public ::
Decoded_rfc4716 | Decoded_openssh_key_v1 |
Decoded_openssh,
Decoded_openssh ::
[{public_key:public_key(),
[{comment, string()}]}],
Decoded_rfc4716 :: [{key(), [{headers, Attrs}]}],
Decoded_openssh_key_v1 ::
experimental_openssh_key_v1_decode(),
Decoded_known_hosts ::
[{public_key:public_key(),
[{comment, string()} | {hostnames,
[string()]}]}],
Decoded_auth_keys ::
[{public_key:public_key(),
[{comment, string()} | {options,
[string()]}]}],
Attrs :: {Key :: string(), Value :: string()}.
Bestie, this one takes an SSH file-binary and decodes it. That's the whole vibe β¨
Okay but here's a lil detail that matters: if Type is public_key, the binary can lowkey be either an RFC4716 public key or an OpenSSH public key. Two formats, one type, the range π€‘
β οΈ NOTE: the implementation of the openssh_key_v1 format is still experimental. Do not come for me if it acts up, it's in its experimental era π
encode β putting it back in the box π
encode(InData, Type) -> binary() | {error, term()}
when
Type ::
ssh2_pubkey | openssh_key | rfc4716_key |
openssh_key_v1 | known_hosts | auth_keys,
InData ::
InData_ssh2_pubkey | InData_openssh |
InData_rfc4716 | InData_openssh_key_v1 |
InData_known_hosts | InData_auth_keys,
InData_ssh2_pubkey :: public_key:public_key(),
InData_openssh ::
[{public_key:public_key(),
[{comment, string()}]}],
InData_rfc4716 :: [{key(), [{headers, Attrs}]}],
InData_openssh_key_v1 ::
experimental_openssh_key_v1_encode(),
InData_known_hosts ::
[{public_key:public_key(),
[{comment, string()} | {hostnames,
[string()]}]}],
InData_auth_keys ::
[{public_key:public_key(),
[{comment, string()} | {options,
[string()]}]}],
Attrs :: {Key :: string(), Value :: string()}.
This one takes a whole list of SSH file entries β that's public keys and their attributes, both β and encodes them into a binary. It's giving β¨*serialization*β¨
β οΈ NOTE: same energy as before, the openssh_key_v1 format implementation is still experimental, bestie. Still cooking π³
extract_public_key β finding the public half of your situationship π
extract_public_key(PrivKey) -> PubKey
when
PrivKey :: public_key:private_key(),
PubKey :: public_key:public_key().
One job, one job only: it fetches the public key out of a private key. Fetches. That's it. Iconic minimalism honestly π
host_key β the server's whole identity π«Ά
host_key(Algorithm, Options) -> Result
when
Algorithm :: ssh:pubkey_alg(),
Result ::
{ok, public_key:private_key()} |
{error, term()},
Options ::
ssh_server_key_api:daemon_key_cb_options(none()).
This implements ssh_server_key_api:host_key/2 β so, yeah, it's the server-side baddie handing out the host's private key π
Options:
system_dir
Files it goes looking through (one per algorithm bestie, the full roster):
SYSDIR/ssh_host_rsa_keySYSDIR/ssh_host_dsa_keySYSDIR/ssh_host_ecdsa_keySYSDIR/ssh_host_ed25519_keySYSDIR/ssh_host_ed448_key
is_auth_key β vibe-checking a key against the list π
is_auth_key(Key, User, Options) -> boolean()
when
Key :: public_key:public_key(),
User :: string(),
Options ::
ssh_server_key_api:daemon_key_cb_options(optimize_key_lookup()).
It implements ssh_server_key_api:is_auth_key/3 β yes, boolean(), a straight yes-or-no, no maybes, no {error, ...}, just truth. Periodt β¨
Options:
user_dir_funuser_dir
Files it peeks at:
USERDIR/authorized_keysUSERDIR/authorized_keys2
And okay, important lil heads up: when this one reads through those files, it discards all the options sitting at the beginning of the lines. Like, thrown out. Bye π So don't be surprised when they're not there in the results π
is_host_key β vibe-checking this host's key π¦
is_host_key(Key, Host, Port, Algorithm, Options) -> Result
when
Key :: public_key:public_key(),
Host ::
inet:ip_address() |
inet:hostname() |
[inet:ip_address() | inet:hostname()],
Port :: inet:port_number(),
Algorithm :: ssh:pubkey_alg(),
Options ::
ssh_client_key_api:client_key_cb_options(optimize_key_lookup()),
Result :: boolean() | {error, term()}.
Implements ssh_client_key_api:is_host_key/5. So it's the client-side twin of the last one, but it does get to say {error, term()} when it's feeling dramatic π
Option:
user_dir
File:
USERDIR/known_hosts
user_key β the keys you carry around everywhere π
user_key(Algorithm, Options) -> Result
when
Algorithm :: ssh:pubkey_alg(),
Result ::
{ok, public_key:private_key()} |
{error, string()},
Options ::
ssh_client_key_api:client_key_cb_options(none()).
This one implements ssh_client_key_api:user_key/2 β your own keys, your own personal bag π
Options:
user_dirdsa_pass_phrasersa_pass_phraseecdsa_pass_phrase
β οΈ Heads up bestie: EdDSA passphrases (Curves25519and448) are not implemented. Not ignoring you on purpose, it's just not there π
Files:
USERDIR/id_dsaUSERDIR/id_rsaUSERDIR/id_ecdsaUSERDIR/id_ed25519USERDIR/id_ed448
aaaand that's the whole roster of functions bestie, every single one accounted for, nothing skipped, no crumbs left π«Ά this lil manual page came to you from Ericsson AB, riding along with ssh 6.0.5 β no date on the footer so I can't give you one, but everything else? got you π
thanks for reading the whole thing, go touch some grass and then come back and read your known_hosts file π β¨ π
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms