Skip to content

Commit

Permalink
ssh options: add various options for SSH connections
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanKarpinski committed Dec 1, 2020
1 parent 462628a commit 191f40c
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 8 deletions.
105 changes: 103 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,112 @@ The default value returned by `ca_roots_path()` may be overridden by setting the
variables, in which case this function will always return the value of the first
of these variables that is set (whether the path exists or not).

### ssh_dir

```jl
ssh_dir() :: String
```
The `ssh_dir()` function returns the location of the directory where the `ssh`
program keeps/looks for configuration files. By default this is `~/.ssh` but
this can be overridden by setting the envirnoment variable `SSH_DIR`.

### ssh_key_name

```jl
ssh_key_name() :: String
```
The `ssh_key_name()` function returns the base name of key files that SSH should
use for when estabilishing a connection. There is usually no reason that this
function should be called directly and libraries should generally use the
`ssh_key_path` and `ssh_pub_key_path` functions to get full paths. If the
environment variable `SSH_KEY_NAME` is set then this function returns that;
otherwise it returns `id_rsa` by default.

### ssh_key_path

```jl
ssh_key_path() :: String
```
The `ssh_key_path()` function returns the path of the SSH private key file that
should be used for SSH connections. If the `SSH_KEY_PATH` environment variable
is set then it will return that value. Otherwise it defaults to returning
```jl
joinpath(ssh_dir(), ssh_key_name())
```
This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME`
environment variables.

### ssh_pub_key_path

```jl
ssh_pub_key_path() :: String
```
The `ssh_pub_key_path()` function returns the path of the SSH public key file
that should be used for SSH connections. If the `SSH_PUB_KEY_PATH` environment
variable is set then it will return that value. If that isn't set but
`SSH_KEY_PATH` is set, it will return that path with the `.pub` suffix appended.
If neither is set, it defaults to returning
```jl
joinpath(ssh_dir(), ssh_key_name() * ".pub")
```
This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME`
environment variables.

### ssh_key_pass

```jl
ssh_key_pass() :: String
```
The `ssh_key_pass()` function returns the value of the environment variable
`SSH_KEY_PASS` if it is set or `nothing` if it is not set. In the future, this
may be able to find a password by other means, such as secure system storage, so
packages that need a pawword to decrypt an SSH private key should use this API
instead of directly checking the environment variable so that they gain such
capabilities automatically when they are added.

### ssh_known_hosts_files

```jl
ssh_known_hosts_files() :: Vector{String}
```
The `ssh_known_hosts_files()` function returns a vector of paths of SSH known
hosts files that should be used when establishing the identities of remote
servers for SSH connections. By default this function returns
```jl
[joinpath(ssh_dir(), "known_hosts"), bundled_known_hosts]
```
where `bundled_known_hosts` is the path of a copy of a known hosts file that is
bundled with this package (containing known hosts keys for `github.com` and
`gitlab.com`). If the environment variable `SSH_KNOWN_HOSTS_FILES` is set,
however, then its value is split into paths on the `:` character (or on `;` on
Windows) and this vector of paths is returned instead. If any component of this
vector is empty, it is expanded to the default known hosts paths.

Packages that use `ssh_known_hosts_files()` should ideally look for matching
entries by comparing the host name and key types, considering the first entry in
any of the files which matches to be the definitive identity of the host. If the
caller cannot compare the key type (e.g. because it has been hashes) then it
must approximate the above algorithm by looking for all matching entries for a
host in each file: if a file has any entries for a host then one of them must
match; the caller should only continue to search further known hosts files if
there are no entries for the host in question in an earlier file.

### ssh_known_hosts_file

```jl
ssh_known_hosts_file() :: String
```
The `ssh_known_hosts_file()` function returns a single path of an SSH known
hosts file that should be used when establishing the identities of remote
servers for SSH connections. It returns the first path returned by
`ssh_known_hosts_files` that actually exists. Callers who can look in more than
one known hosts file should use `ssh_known_hosts_files` instead and look for
host matches in all the files returned as described in that function's docs.

### verify_host

```jl
verify_host(url::AbstractString, transport::AbstractString) -> Bool
verify_host(url::AbstractString) -> Bool
verify_host(url::AbstractString, [transport::AbstractString]) :: Bool
```
The `verify_host` function tells the caller whether the identity of a host
should be verified when communicating over secure transports like TLS or SSH.
Expand Down
1 change: 1 addition & 0 deletions src/NetworkOptions.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module NetworkOptions

include("ca_roots.jl")
include("ssh_options.jl")
include("verify_host.jl")

end # module
4 changes: 2 additions & 2 deletions src/ca_roots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ const CA_ROOTS_VARS = [

function _ca_roots(allow_nothing::Bool)
for var in CA_ROOTS_VARS
path = get(ENV, var, nothing)
path !== nothing && !isempty(path) && return path
path = get(ENV, var, "")
!isempty(path) && return path
end
if Sys.iswindows() || Sys.isapple()
allow_nothing && return # use system certs
Expand Down
4 changes: 4 additions & 0 deletions src/known_hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf
gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
142 changes: 142 additions & 0 deletions src/ssh_options.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
export
ssh_dir,
ssh_key_pass,
ssh_key_name,
ssh_key_path,
ssh_pub_key_path,
ssh_known_hosts_files,
ssh_known_hosts_file

"""
ssh_dir() :: String
The `ssh_dir()` function returns the location of the directory where the `ssh`
program keeps/looks for configuration files. By default this is `~/.ssh` but
this can be overridden by setting the envirnoment variable `SSH_DIR`.
"""
ssh_dir() = get(ENV, "SSH_DIR", joinpath(homedir(), ".ssh"))

"""
ssh_key_pass() :: String
The `ssh_key_pass()` function returns the value of the environment variable
`SSH_KEY_PASS` if it is set or `nothing` if it is not set. In the future, this
may be able to find a password by other means, such as secure system storage, so
packages that need a pawword to decrypt an SSH private key should use this API
instead of directly checking the environment variable so that they gain such
capabilities automatically when they are added.
"""
ssh_key_pass() = get(ENV, "SSH_KEY_PASS", nothing)

"""
ssh_key_name() :: String
The `ssh_key_name()` function returns the base name of key files that SSH should
use for when estabilishing a connection. There is usually no reason that this
function should be called directly and libraries should generally use the
`ssh_key_path` and `ssh_pub_key_path` functions to get full paths. If the
environment variable `SSH_KEY_NAME` is set then this function returns that;
otherwise it returns `id_rsa` by default.
"""
ssh_key_name() = get(ENV, "SSH_KEY_NAME", "id_rsa")

"""
ssh_key_path() :: String
The `ssh_key_path()` function returns the path of the SSH private key file that
should be used for SSH connections. If the `SSH_KEY_PATH` environment variable
is set then it will return that value. Otherwise it defaults to returning
joinpath(ssh_dir(), ssh_key_name())
This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME`
environment variables.
"""
function ssh_key_path()
key_path = get(ENV, "SSH_KEY_PATH", "")
!isempty(key_path) && return key_path
return joinpath(ssh_dir(), ssh_key_name())
end

"""
ssh_pub_key_path() :: String
The `ssh_pub_key_path()` function returns the path of the SSH public key file
that should be used for SSH connections. If the `SSH_PUB_KEY_PATH` environment
variable is set then it will return that value. If that isn't set but
`SSH_KEY_PATH` is set, it will return that path with the `.pub` suffix appended.
If neither is set, it defaults to returning
joinpath(ssh_dir(), ssh_key_name() * ".pub")
This default value in turn depends on the `SSH_DIR` and `SSH_KEY_NAME`
environment variables.
"""
function ssh_pub_key_path()
pub_key_path = get(ENV, "SSH_PUB_KEY_PATH", "")
!isempty(pub_key_path) && return pub_key_path
key_path = get(ENV, "SSH_KEY_PATH", "")
!isempty(key_path) && return "$key_path.pub"
return joinpath(ssh_dir(), ssh_key_name() * ".pub")
end

"""
ssh_known_hosts_files() :: Vector{String}
The `ssh_known_hosts_files()` function returns a vector of paths of SSH known
hosts files that should be used when establishing the identities of remote
servers for SSH connections. By default this function returns
[joinpath(ssh_dir(), "known_hosts"), bundled_known_hosts]
where `bundled_known_hosts` is the path of a copy of a known hosts file that is
bundled with this package (containing known hosts keys for `github.com` and
`gitlab.com`). If the environment variable `SSH_KNOWN_HOSTS_FILES` is set,
however, then its value is split into paths on the `:` character (or on `;` on
Windows) and this vector of paths is returned instead. If any component of this
vector is empty, it is expanded to the default known hosts paths.
Packages that use `ssh_known_hosts_files()` should ideally look for matching
entries by comparing the host name and key types, considering the first entry in
any of the files which matches to be the definitive identity of the host. If the
caller cannot compare the key type (e.g. because it has been hashes) then it
must approximate the above algorithm by looking for all matching entries for a
host in each file: if a file has any entries for a host then one of them must
match; the caller should only continue to search further known hosts files if
there are no entries for the host in question in an earlier file.
"""
function ssh_known_hosts_files()
default = joinpath(ssh_dir(), "known_hosts")
bundled = joinpath(@__DIR__, "known_hosts")
value = get(ENV, "SSH_KNOWN_HOSTS_FILES", nothing)
value === nothing && return [default, bundled]
isempty(value) && return String[]
paths = String[]
for path in split(value, Sys.iswindows() ? ';' : ':')
if !isempty(path)
path in paths || push!(paths, path)
else
default in paths || push!(paths, default)
bundled in paths || push!(paths, bundled)
end
end
return paths
end

"""
ssh_known_hosts_file() :: String
The `ssh_known_hosts_file()` function returns a single path of an SSH known
hosts file that should be used when establishing the identities of remote
servers for SSH connections. It returns the first path returned by
`ssh_known_hosts_files` that actually exists. Callers who can look in more than
one known hosts file should use `ssh_known_hosts_files` instead and look for
host matches in all the files returned as described in that function's docs.
"""
function ssh_known_hosts_file()
files = ssh_known_hosts_files()
for file in files
ispath(file) && return file
end
return files[1]
end
2 changes: 2 additions & 0 deletions src/verify_host.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export verify_host

"""
verify_host(url::AbstractString, [transport::AbstractString]) :: Bool
The `verify_host` function tells the caller whether the identity of a host
should be verified when communicating over secure transports like TLS or SSH.
The `url` argument may be:
Expand Down
Loading

0 comments on commit 191f40c

Please sign in to comment.