Documentation ¶
Overview ¶
Package knownhosts is a thin wrapper around golang.org/x/crypto/ssh/knownhosts, adding the ability to obtain the list of host key algorithms for a known host.
Index ¶
- func HostKeyAlgorithms(cb ssh.HostKeyCallback, hostWithPort string) []string
- func IsHostKeyChanged(err error) bool
- func IsHostUnknown(err error) bool
- func Line(addresses []string, key ssh.PublicKey) string
- func Normalize(address string) string
- func WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error
- func WriteKnownHostCA(w io.Writer, hostPattern string, key ssh.PublicKey) error
- type HostKeyCallback
- type HostKeyDB
- type PublicKey
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HostKeyAlgorithms ¶
func HostKeyAlgorithms(cb ssh.HostKeyCallback, hostWithPort string) []string
HostKeyAlgorithms is a convenience function for performing host key algorithm lookups on an ssh.HostKeyCallback directly. It is intended for use in code paths that stay with the New method of golang.org/x/crypto/ssh/knownhosts rather than this package's New or NewDB methods. The returned values will not include ssh.CertAlgo* values. If any known_hosts lines had @cert-authority prefixes, their original key algo will be returned instead. For proper CA support, see NewDB and HostKeyDB.HostKeyAlgorithms instead.
func IsHostKeyChanged ¶ added in v1.1.0
IsHostKeyChanged returns a boolean indicating whether the error indicates the host key has changed. It is intended to be called on the error returned from invoking a host key callback, to check whether an SSH host is known.
func IsHostUnknown ¶ added in v1.1.0
IsHostUnknown returns a boolean indicating whether the error represents an unknown host. It is intended to be called on the error returned from invoking a host key callback to check whether an SSH host is known.
func Line ¶ added in v1.2.0
Line returns a line to append to the known_hosts files. This implementation uses the local patched implementation of Normalize in order to solve https://github.com/golang/go/issues/53463.
func Normalize ¶ added in v1.2.0
Normalize normalizes an address into the form used in known_hosts. This implementation includes a fix for https://github.com/golang/go/issues/53463 and will omit brackets around ipv6 addresses on standard port 22.
func WriteKnownHost ¶ added in v1.1.0
WriteKnownHost writes a known_hosts line to w for the supplied hostname, remote, and key. This is useful when writing a custom hostkey callback which wraps a callback obtained from this package to provide additional known_hosts management functionality. The hostname, remote, and key typically correspond to the callback's args. This function does not support writing @cert-authority lines.
Example ¶
package main import ( "fmt" "log" "net" "os" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { sshHost := "yourserver.com:22" khPath := "/home/myuser/.ssh/known_hosts" kh, err := knownhosts.NewDB(khPath) if err != nil { log.Fatal("Failed to read known_hosts: ", err) } // Create a custom permissive hostkey callback which still errors on hosts // with changed keys, but allows unknown hosts and adds them to known_hosts cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { innerCallback := kh.HostKeyCallback() err := innerCallback(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for host %s! This may indicate a MitM attack.", hostname) } else if knownhosts.IsHostUnknown(err) { f, ferr := os.OpenFile(khPath, os.O_APPEND|os.O_WRONLY, 0600) if ferr == nil { defer f.Close() ferr = knownhosts.WriteKnownHost(f, hostname, remote, key) } if ferr == nil { log.Printf("Added host %s to known_hosts\n", hostname) } else { log.Printf("Failed to add host %s to known_hosts: %v\n", hostname, ferr) } return nil // permit previously-unknown hosts (warning: may be insecure) } return err }) config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, HostKeyCallback: cb, HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal("Failed to dial: ", err) } defer client.Close() }
Output:
Types ¶
type HostKeyCallback ¶
type HostKeyCallback ssh.HostKeyCallback
HostKeyCallback wraps ssh.HostKeyCallback with additional methods to perform host key and algorithm lookups from the known_hosts entries. It is otherwise identical to ssh.HostKeyCallback, and does not introduce any file- parsing behavior beyond what is in golang.org/x/crypto/ssh/knownhosts.
In most situations, use HostKeyDB and its constructor NewDB instead of using the HostKeyCallback type. The HostKeyCallback type is only provided for backwards compatibility with older versions of this package, as well as for very strict situations where any extra known_hosts file-parsing is undesirable.
Methods of HostKeyCallback do not provide any special treatment for @cert-authority lines, which will (incorrectly) look like normal non-CA host keys. Additionally, HostKeyCallback lacks the fix for applying * wildcard known_host entries to all ports, like OpenSSH's behavior.
func New ¶
func New(files ...string) (HostKeyCallback, error)
New creates a HostKeyCallback from the given OpenSSH known_hosts file(s). The returned value may be used in ssh.ClientConfig.HostKeyCallback by casting it to ssh.HostKeyCallback, or using its HostKeyCallback method. Otherwise, it operates the same as the New function in golang.org/x/crypto/ssh/knownhosts. When supplying multiple files, their order does not matter.
In most situations, you should avoid this function, as the returned value lacks several enhanced behaviors. See doc comment for HostKeyCallback for more information. Instead, most callers should use NewDB to create a HostKeyDB, which includes these enhancements.
Example ¶
package main import ( "log" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { sshHost := "yourserver.com:22" kh, err := knownhosts.New("/home/myuser/.ssh/known_hosts") if err != nil { log.Fatal("Failed to read known_hosts: ", err) } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal("Failed to dial: ", err) } defer client.Close() }
Output:
func (HostKeyCallback) HostKeyAlgorithms ¶
func (hkcb HostKeyCallback) HostKeyAlgorithms(hostWithPort string) (algos []string)
HostKeyAlgorithms returns a slice of host key algorithms for the supplied host:port found in the known_hosts file(s), or an empty slice if the host is not already known. The result may be used in ssh.ClientConfig's HostKeyAlgorithms field, either as-is or after filtering (if you wish to ignore or prefer particular algorithms). For hosts that have multiple known_hosts entries (for different key types), the result will be sorted by known_hosts filename and line number. The returned values will not include ssh.CertAlgo* values. If any known_hosts lines had @cert-authority prefixes, their original key algo will be returned instead. For proper CA support, see NewDB and HostKeyDB.HostKeyAlgorithms instead.
func (HostKeyCallback) HostKeyCallback ¶
func (hkcb HostKeyCallback) HostKeyCallback() ssh.HostKeyCallback
HostKeyCallback simply casts the receiver back to ssh.HostKeyCallback, for use in ssh.ClientConfig.HostKeyCallback.
func (HostKeyCallback) HostKeys ¶ added in v1.1.0
func (hkcb HostKeyCallback) HostKeys(hostWithPort string) []ssh.PublicKey
HostKeys returns a slice of known host public keys for the supplied host:port found in the known_hosts file(s), or an empty slice if the host is not already known. For hosts that have multiple known_hosts entries (for different key types), the result will be sorted by known_hosts filename and line number. In the returned values, there is no way to distinguish between CA keys (known_hosts lines beginning with @cert-authority) and regular keys. To do so, see NewDB and HostKeyDB.HostKeys instead.
func (HostKeyCallback) ToDB ¶ added in v1.3.0
func (hkcb HostKeyCallback) ToDB() *HostKeyDB
ToDB converts the receiver into a HostKeyDB. However, the returned HostKeyDB lacks the enhanced behaviors described in the doc comment for NewDB: proper CA support, and wildcard matching on nonstandard ports.
It is generally preferable to create a HostKeyDB by using NewDB. The ToDB method is only provided for situations in which the calling code needs to make the extra NewDB behaviors optional / user-configurable, perhaps for reasons of performance or code trust (since NewDB reads the known_host file an extra time, which may be undesirable in some strict situations). This way, callers can conditionally create a non-enhanced HostKeyDB by using New and ToDB. See code example.
Example ¶
package main import ( "log" "os" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { khFile := "/home/myuser/.ssh/known_hosts" var kh *knownhosts.HostKeyDB var err error // Example of using conditional logic to determine whether or not to perform // extra parsing pass on the known_hosts file in order to enable enhanced // behaviors if os.Getenv("SKIP_KNOWNHOSTS_ENHANCEMENTS") != "" { // Create a HostKeyDB using New + ToDB: this will skip the extra known_hosts // processing var cb knownhosts.HostKeyCallback if cb, err = knownhosts.New(khFile); err == nil { kh = cb.ToDB() } } else { // Create a HostKeyDB using NewDB: this will perform extra known_hosts // processing, allowing proper support for CAs, as well as OpenSSH-like // wildcard matching on non-standard ports kh, err = knownhosts.NewDB(khFile) } if err != nil { log.Fatal("Failed to read known_hosts: ", err) } sshHost := "yourserver.com:22" config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal("Failed to dial: ", err) } defer client.Close() }
Output:
type HostKeyDB ¶ added in v1.3.0
type HostKeyDB struct {
// contains filtered or unexported fields
}
HostKeyDB wraps logic in golang.org/x/crypto/ssh/knownhosts with additional behaviors, such as the ability to perform host key/algorithm lookups from known_hosts entries.
func NewDB ¶ added in v1.3.0
NewDB creates a HostKeyDB from the given OpenSSH known_hosts file(s). It reads and parses the provided files one additional time (beyond logic in golang.org/x/crypto/ssh/knownhosts) in order to:
- Handle CA lines properly and return ssh.CertAlgo* values when calling the HostKeyAlgorithms method, for use in ssh.ClientConfig.HostKeyAlgorithms
- Allow * wildcards in hostnames to match on non-standard ports, providing a workaround for https://github.com/golang/go/issues/52056 in order to align with OpenSSH's wildcard behavior
When supplying multiple files, their order does not matter.
Example ¶
package main import ( "log" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { sshHost := "yourserver.com:22" kh, err := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") if err != nil { log.Fatal("Failed to read known_hosts: ", err) } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal("Failed to dial: ", err) } defer client.Close() }
Output:
func (*HostKeyDB) HostKeyAlgorithms ¶ added in v1.3.0
HostKeyAlgorithms returns a slice of host key algorithms for the supplied host:port found in the known_hosts file(s), or an empty slice if the host is not already known. The result may be used in ssh.ClientConfig's HostKeyAlgorithms field, either as-is or after filtering (if you wish to ignore or prefer particular algorithms). For hosts that have multiple known_hosts entries (of different key types), the result will be sorted by known_hosts filename and line number. If hkdb was originally created by calling NewDB, any @cert-authority lines in the known_hosts file will properly be converted to the corresponding ssh.CertAlgo* values.
func (*HostKeyDB) HostKeyCallback ¶ added in v1.3.0
func (hkdb *HostKeyDB) HostKeyCallback() ssh.HostKeyCallback
HostKeyCallback returns an ssh.HostKeyCallback. This can be used directly in ssh.ClientConfig.HostKeyCallback, as shown in the example for NewDB. Alternatively, you can wrap it with an outer callback to potentially handle appending a new entry to the known_hosts file; see example in WriteKnownHost.
func (*HostKeyDB) HostKeys ¶ added in v1.3.0
HostKeys returns a slice of known host public keys for the supplied host:port found in the known_hosts file(s), or an empty slice if the host is not already known. For hosts that have multiple known_hosts entries (for different key types), the result will be sorted by known_hosts filename and line number. If hkdb was originally created by calling NewDB, the Cert boolean field of each result entry reports whether the key corresponded to a @cert-authority line. If hkdb was NOT obtained from NewDB, then Cert will always be false.