### Get HostKeyCallback from HostKeyDB Source: https://context7.com/skeema/knownhosts/llms.txt Obtain an ssh.HostKeyCallback from a HostKeyDB for use in ssh.ClientConfig.HostKeyCallback. This callback correctly handles OpenSSH's wildcard-matching semantics for non-standard ports when using NewDB. ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") // Use directly in ClientConfig config := &ssh.ClientConfig{ HostKeyCallback: kh.HostKeyCallback(), } // Or wrap it to add custom logic (see WriteKnownHost example below) cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { return fmt.Errorf("host key changed for %s – possible MitM", hostname) } return err }) config2 := &ssh.ClientConfig{ HostKeyCallback: cb, } _ = config2 ``` -------------------------------- ### Create HostKeyDB with NewDB Source: https://context7.com/skeema/knownhosts/llms.txt Use NewDB to create a *HostKeyDB for enhanced known_hosts management, including CA-aware algorithm lookup and wildcard matching. This is the recommended constructor for new code. ```go import ( "log" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func sshClientConfig(hostWithPort string) (*ssh.ClientConfig, error) { // Load one or more known_hosts files kh, err := knownhosts.NewDB( "/home/myuser/.ssh/known_hosts", "/etc/ssh/ssh_known_hosts", // optional second file ) if err != nil { return nil, err } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ ssh.Password("secret"), }, // HostKeyCallback validates the server's presented key against known_hosts. HostKeyCallback: kh.HostKeyCallback(), // HostKeyAlgorithms tells the SSH client which algorithms to advertise, // preventing the "no common algorithms" error when a host has multiple keys. HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort), } return config, nil } func main() { host := "yourserver.com:22" cfg, err := sshClientConfig(host) if err != nil { log.Fatal(err) } client, err := ssh.Dial("tcp", host, cfg) if err != nil { log.Fatal("dial:", err) } defer client.Close() // client is now a fully authenticated *ssh.Client } ``` -------------------------------- ### New — Create a HostKeyCallback (legacy / minimal parsing) Source: https://context7.com/skeema/knownhosts/llms.txt `New` mirrors `golang.org/x/crypto/ssh/knownhosts.New` but returns a `HostKeyCallback` with extra methods. It does not perform the additional parse pass, so CA lines and non-standard-port wildcards are not handled correctly. Prefer `NewDB`. ```APIDOC ## New ### Description Mirrors the call pattern of `golang.org/x/crypto/ssh/knownhosts.New` but returns a `HostKeyCallback` value with extra methods. It does **not** perform the additional parse pass, so CA lines and non-standard-port wildcards are not handled correctly. Prefer `NewDB` unless the extra file read is explicitly undesirable. ### Method `knownhosts.New(path string) (*HostKeyDB, error)` ### Parameters #### Path Parameters - **path** (*string) - Required - Path to the known_hosts file to parse. ### Request Example ```go 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(err) } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ssh.Password("secret")}, HostKeyCallback: kh.HostKeyCallback(), // cast to ssh.HostKeyCallback HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal(err) } defer client.Close() } ``` ### Response #### Success Response (200) - **HostKeyDB** (*HostKeyDB) - A HostKeyDB object with minimal parsing. - **error** (*error) - An error if parsing fails. ``` -------------------------------- ### NewDB — Create a HostKeyDB with full enhancements Source: https://context7.com/skeema/knownhosts/llms.txt `NewDB` parses known_hosts files to return a `*HostKeyDB` supporting CA-aware algorithm lookup, wildcard matching, and host key queries. This is the recommended constructor. ```APIDOC ## NewDB ### Description Parses one or more known_hosts files and returns a `*HostKeyDB` that supports CA-aware algorithm lookup, wildcard matching on non-standard ports, and host key queries. This is the recommended constructor for all new code. ### Method `knownhosts.NewDB(paths ...string) (*HostKeyDB, error)` ### Parameters #### Path Parameters - **paths** (*string) - Variadic - Paths to known_hosts files to parse. ### Request Example ```go import ( "log" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func sshClientConfig(hostWithPort string) (*ssh.ClientConfig, error) { // Load one or more known_hosts files kh, err := knownhosts.NewDB( "/home/myuser/.ssh/known_hosts", "/etc/ssh/ssh_known_hosts", // optional second file ) if err != nil { return nil, err } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ ssh.Password("secret"), }, // HostKeyCallback validates the server's presented key against known_hosts. HostKeyCallback: kh.HostKeyCallback(), // HostKeyAlgorithms tells the SSH client which algorithms to advertise, // preventing the "no common algorithms" error when a host has multiple keys. HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort), } return config, nil } ``` ### Response #### Success Response (200) - **HostKeyDB** (*HostKeyDB) - A HostKeyDB object with enhanced capabilities. - **error** (*error) - An error if parsing fails. ``` -------------------------------- ### Create HostKeyCallback with New (Legacy) Source: https://context7.com/skeema/knownhosts/llms.txt Use New for legacy compatibility, mirroring golang.org/x/crypto/ssh/knownhosts.New. It does not perform enhanced parsing for CA lines or non-standard-port wildcards. Prefer NewDB unless the extra file read is undesirable. ```go 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(err) } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ssh.Password("secret")}, HostKeyCallback: kh.HostKeyCallback(), // cast to ssh.HostKeyCallback HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost), } client, err := ssh.Dial("tcp", sshHost, config) if err != nil { log.Fatal(err) } defer client.Close() } ``` -------------------------------- ### Create SSH ClientConfig with HostKeyAlgorithms Source: https://github.com/skeema/knownhosts/blob/main/README.md Use this function to create an ssh.ClientConfig that automatically populates HostKeyAlgorithms based on the provided known_hosts file. This helps resolve issues where a host's first public key is not in known_hosts but another key type is. ```go import ( "golang.org/x/crypto/ssh" "github.com/skeema/knownhosts" ) func sshConfigForHost(hostWithPort string) (*ssh.ClientConfig, error) { kh, err := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") if err != nil { return nil, err } config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ /* ... */ }, HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms(hostWithPort), } return config, nil } ``` -------------------------------- ### Create Permissive Host Key Callback with Known Hosts DB Source: https://github.com/skeema/knownhosts/blob/main/README.md Use this to create a custom host key callback that allows unknown hosts and automatically adds them to the known_hosts file, while still erroring on changed keys. Requires the `knownhosts.NewDB` constructor and careful handling of file operations. ```golang 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), } ``` -------------------------------- ### Convert Legacy HostKeyCallback to HostKeyDB Source: https://context7.com/skeema/knownhosts/llms.txt Converts a `knownhosts.HostKeyCallback` to a `*HostKeyDB` without an additional file parse. This allows opting into enhanced behaviors like CA and wildcard support at runtime. It's useful for integrating with existing SSH configurations. ```go import "os" func buildHostKeyDB(khFile string) (*knownhosts.HostKeyDB, error) { if os.Getenv("SKIP_KNOWNHOSTS_ENHANCEMENTS") != "" { // Minimal path: no extra file parse cb, err := knownhosts.New(khFile) if err != nil { return nil, err } return cb.ToDB(), nil } // Full path: extra parse for CA + wildcard support return knownhosts.NewDB(khFile) } func main() { kh, err := buildHostKeyDB("/home/myuser/.ssh/known_hosts") if err != nil { log.Fatal(err) } config := &ssh.ClientConfig{ HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms("yourserver.com:22"), } _ = config } ``` -------------------------------- ### Query Host Key Algorithms for a Host Source: https://context7.com/skeema/knownhosts/llms.txt Obtains the recommended host-key algorithm strings for a specific host and port, suitable for `ssh.ClientConfig.HostKeyAlgorithms`. This helps prevent 'no common algorithms' errors. RSA keys are expanded to include SHA2 variants. ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") algos := kh.HostKeyAlgorithms("yourserver.com:22") fmt.Println(algos) config := &ssh.ClientConfig{ HostKeyAlgorithms: algos, // prevents "no common algorithms" errors } _ = config ``` -------------------------------- ### HostKeyCallback.ToDB Source: https://context7.com/skeema/knownhosts/llms.txt Converts a legacy `HostKeyCallback` into a `*HostKeyDB` without re-parsing the known_hosts file, allowing for opt-in enhanced features. ```APIDOC ## HostKeyCallback.ToDB — Convert a legacy callback to HostKeyDB ### Description Converts a `HostKeyCallback` (obtained from `New`) into a `*HostKeyDB` without performing the extra parse pass. Useful when you want to make the enhanced behaviors opt-in at runtime. ### Method `ToDB() *HostKeyDB` ### Parameters None ### Response #### Success Response (*HostKeyDB) - **HostKeyDB** (*knownhosts.HostKeyDB) - A pointer to the HostKeyDB instance. ### Request Example ```go import "os" func buildHostKeyDB(khFile string) (*knownhosts.HostKeyDB, error) { if os.Getenv("SKIP_KNOWNHOSTS_ENHANCEMENTS") != "" { // Minimal path: no extra file parse cb, err := knownhosts.New(khFile) if err != nil { return nil, err } return cb.ToDB(), nil } // Full path: extra parse for CA + wildcard support return knownhosts.NewDB(khFile) } func main() { kh, err := buildHostKeyDB("/home/myuser/.ssh/known_hosts") if err != nil { log.Fatal(err) } config := &ssh.ClientConfig{ HostKeyCallback: kh.HostKeyCallback(), HostKeyAlgorithms: kh.HostKeyAlgorithms("yourserver.com:22"), } _ = config } ``` ``` -------------------------------- ### HostKeyDB.HostKeyAlgorithms Source: https://context7.com/skeema/knownhosts/llms.txt Retrieves the recommended SSH host-key algorithm strings for a given host and port, suitable for use in `ssh.ClientConfig.HostKeyAlgorithms`. ```APIDOC ## HostKeyDB.HostKeyAlgorithms — Query algorithm strings for a host ### Description Returns the slice of host-key algorithm strings to use in `ssh.ClientConfig.HostKeyAlgorithms` for the given `host:port`. RSA keys automatically expand to `rsa-sha2-512`, `rsa-sha2-256`, and `ssh-rsa` in that order. CA entries are translated to their `ssh.CertAlgo*` equivalents when using `NewDB`. ### Method `HostKeyAlgorithms(host string)` ### Parameters #### Path Parameters - **host** (string) - Required - The host and port to query (e.g., "yourserver.com:22"). ### Response #### Success Response (Slice of strings) - **algorithms** (slice of string) - A list of SSH host-key algorithm strings. ### Request Example ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") algos := kh.HostKeyAlgorithms("yourserver.com:22") fmt.Println(algos) config := &ssh.ClientConfig{ HostKeyAlgorithms: algos, // prevents "no common algorithms" errors } _ = config ``` ### Response Example ``` [rsa-sha2-512-cert-v01@openssh.com rsa-sha2-256-cert-v01@openssh.com ssh-rsa-cert-v01@openssh.com ssh-ed25519] ``` ``` -------------------------------- ### Detect First-Time (Unknown) Hosts Source: https://context7.com/skeema/knownhosts/llms.txt Checks if an `ssh.HostKeyCallback` error indicates that the host is not found in any known_hosts file. This is used with `WriteKnownHost` to implement `StrictHostKeyChecking=no/ask` behavior, allowing new hosts to be added. ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostUnknown(err) { fmt.Printf("Unknown host %s – key type %s\n", hostname, key.Type()) // Could prompt user for confirmation here return nil // accept if desired } return err }) _ = cb ``` -------------------------------- ### Query Known Public Keys for a Host Source: https://context7.com/skeema/knownhosts/llms.txt Retrieves all known public keys for a given host and port. The `Cert` field indicates if the entry was a certificate authority. Use this to verify host identity against known entries. ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") keys := kh.HostKeys("yourserver.com:22") for _, k := range keys { fmt.Printf("type=%s cert=%v fingerprint=%s\n", k.Type(), k.Cert, ssh.FingerprintSHA256(k), ) } ``` -------------------------------- ### Format Known Hosts Line String Source: https://context7.com/skeema/knownhosts/llms.txt Returns a single known_hosts-format line string for a list of addresses and a public key, without a trailing newline. Uses the package's patched `Normalize` to avoid IPv6 formatting bugs. ```go import ( "fmt" "golang.org/x/crypto/ssh" "github.com/skeema/knownhosts" ) func printKnownHostLine(key ssh.PublicKey) { addresses := []string{"yourserver.com:22", "203.0.113.10"} line := knownhosts.Line(addresses, key) fmt.Println(line) // Output: yourserver.com,203.0.113.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... } ``` -------------------------------- ### Write @cert-authority Entry to io.Writer Source: https://context7.com/skeema/knownhosts/llms.txt Appends a `@cert-authority` line to any io.Writer for a given host pattern and CA public key. Use this when bootstrapping a known_hosts file that trusts a certificate authority rather than individual host keys. ```go import ( "os" "golang.org/x/crypto/ssh" "github.com/skeema/knownhosts" ) func trustCA(hostPattern string, caPubKey ssh.PublicKey) error { f, err := os.OpenFile("/home/myuser/.ssh/known_hosts", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } defer f.Close() // Writes: @cert-authority *.example.com ssh-rsa AAAA... return knownhosts.WriteKnownHostCA(f, hostPattern, caPubKey) } ``` -------------------------------- ### HostKeyAlgorithms Source: https://context7.com/skeema/knownhosts/llms.txt Looks up host key algorithms directly on an ssh.HostKeyCallback without needing a HostKeyDB. This is useful for code paths using golang.org/x/crypto/ssh/knownhosts.New directly. ```APIDOC ## HostKeyAlgorithms (package-level function) ### Description A convenience function that performs host key algorithm lookup directly on any `ssh.HostKeyCallback` without needing to create a `HostKeyDB`. Intended for code paths that use `golang.org/x/crypto/ssh/knownhosts.New` directly. Does **not** translate CA entries to `ssh.CertAlgo*` values. ### Method `knownhosts.HostKeyAlgorithms(cb ssh.HostKeyCallback, host string) []string` ### Parameters - **cb** (ssh.HostKeyCallback) - The host key callback to query. - **host** (string) - The host and port to look up algorithms for (e.g., "yourserver.com:22"). ### Response - **[]string** - A slice of supported host key algorithm names. ### Request Example ```go import ( xknownhosts "golang.org/x/crypto/ssh/knownhosts" "github.com/skeema/knownhosts" ) cb, _ := xknownhosts.New("/home/myuser/.ssh/known_hosts") algos := knownhosts.HostKeyAlgorithms(cb, "yourserver.com:22") fmt.Println(algos) // ["rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-ed25519"] ``` ``` -------------------------------- ### Host Key Algorithms Lookup on Raw Callback Source: https://context7.com/skeema/knownhosts/llms.txt Performs host key algorithm lookup directly on any ssh.HostKeyCallback without needing to create a HostKeyDB. Intended for code paths that use golang.org/x/crypto/ssh/knownhosts.New directly. Does not translate CA entries to ssh.CertAlgo* values. ```go import ( xknownhosts "golang.org/x/crypto/ssh/knownhosts" "github.com/skeema/knownhosts" ) cb, _ := xknownhosts.New("/home/myuser/.ssh/known_hosts") algos := knownhosts.HostKeyAlgorithms(cb, "yourserver.com:22") fmt.Println(algos) // ["rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-ed25519"] ``` -------------------------------- ### HostKeyDB.HostKeyCallback — Obtain the ssh.HostKeyCallback Source: https://context7.com/skeema/knownhosts/llms.txt Returns an `ssh.HostKeyCallback` suitable for direct use in `ssh.ClientConfig.HostKeyCallback`. When `HostKeyDB` was created with `NewDB`, the returned callback applies OpenSSH's wildcard-matching semantics for non-standard ports. ```APIDOC ## HostKeyDB.HostKeyCallback ### Description Obtains an `ssh.HostKeyCallback` from the `HostKeyDB`. This callback can be directly used in `ssh.ClientConfig.HostKeyCallback`. If the `HostKeyDB` was initialized using `NewDB`, this callback correctly handles wildcard entries for non-standard ports, aligning with OpenSSH's behavior. ### Method `(*HostKeyDB).HostKeyCallback() ssh.HostKeyCallback` ### Parameters None ### Request Example ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") // Use directly in ClientConfig config := &ssh.ClientConfig{ HostKeyCallback: kh.HostKeyCallback(), } // Or wrap it to add custom logic (see WriteKnownHost example below) cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { return fmt.Errorf("host key changed for %s – possible MitM", hostname) } return err }) config2 := &ssh.ClientConfig{ HostKeyCallback: cb, } _ = config2 ``` ### Response #### Success Response (200) - **ssh.HostKeyCallback** - A function that validates the server's public key against the known hosts. ``` -------------------------------- ### WriteKnownHost Source: https://context7.com/skeema/knownhosts/llms.txt Appends a new entry to the known_hosts file for a given hostname, remote address, and public key. It normalizes addresses and includes both symbolic hostname and IP address when they differ. ```APIDOC ## WriteKnownHost ### Description Appends a new entry to known_hosts for the given hostname, remote address, and public key. Normalizes addresses (including IPv6 fixes) and includes both the symbolic hostname and the IP address when they differ. ### Method `knownhosts.WriteKnownHost(w io.Writer, hostname string, remote net.Addr, key ssh.PublicKey) error` ### Parameters - **w** (io.Writer) - The writer to append the known_hosts line to. - **hostname** (string) - The hostname to record. - **remote** (net.Addr) - The remote network address. - **key** (ssh.PublicKey) - The public key of the remote host. ### Request Example ```go import ( "fmt" "log" "net" "os" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { khPath := "/home/myuser/.ssh/known_hosts" kh, err := knownhosts.NewDB(khPath) if err != nil { log.Fatal(err) } cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { innerErr := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(innerErr) { return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for %s", hostname) } if knownhosts.IsHostUnknown(innerErr) { 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", hostname) } else { log.Printf("Failed to add %s: %v", hostname, ferr) } return nil // permit unknown host } return innerErr }) config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ssh.Password("secret")}, HostKeyCallback: cb, HostKeyAlgorithms: kh.HostKeyAlgorithms("yourserver.com:22"), } client, err := ssh.Dial("tcp", "yourserver.com:22", config) if err != nil { log.Fatal(err) } defer client.Close() } ``` ``` -------------------------------- ### IsHostUnknown Source: https://context7.com/skeema/knownhosts/llms.txt Detects if a host is encountered for the first time (i.e., not present in any known_hosts file). This is useful for implementing `StrictHostKeyChecking=no/ask` behavior. ```APIDOC ## IsHostUnknown — Detect first-time (unknown) hosts ### Description Returns `true` when an error returned by an `ssh.HostKeyCallback` indicates the host is not present in any known_hosts file at all. Used together with `WriteKnownHost` to implement `StrictHostKeyChecking=no/ask` behavior. ### Method `IsHostUnknown(err error) bool` ### Parameters #### Path Parameters - **err** (error) - Required - The error returned from an `ssh.HostKeyCallback`. ### Response #### Success Response (boolean) - **true** if the host is unknown, **false** otherwise. ### Request Example ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostUnknown(err) { fmt.Printf("Unknown host %s – key type %s\n", hostname, key.Type()) // Could prompt user for confirmation here return nil // accept if desired } return err }) _ = cb ``` ``` -------------------------------- ### Line Source: https://context7.com/skeema/knownhosts/llms.txt Formats a list of addresses and a public key into a single known_hosts line string without a trailing newline. It uses a patched normalization function to avoid IPv6 formatting bugs. ```APIDOC ## Line ### Description Returns a single known_hosts-format line string for a list of addresses and a public key, without a trailing newline. Uses the package's patched `Normalize` to avoid IPv6 formatting bugs present in older `golang.org/x/crypto` versions. ### Method `knownhosts.Line(addresses []string, key ssh.PublicKey) string` ### Parameters - **addresses** ([]string) - A slice of network addresses (e.g., "yourserver.com:22", "203.0.113.10"). - **key** (ssh.PublicKey) - The public key of the host. ### Response - **string** - A single line formatted for the known_hosts file. ### Request Example ```go import ( "fmt" "golang.org/x/crypto/ssh" "github.com/skeema/knownhosts" ) func printKnownHostLine(key ssh.PublicKey) { addresses := []string{"yourserver.com:22", "203.0.113.10"} line := knownhosts.Line(addresses, key) fmt.Println(line) // Output: yourserver.com,203.0.113.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... } ``` ``` -------------------------------- ### Write Known Host Entry to io.Writer Source: https://context7.com/skeema/knownhosts/llms.txt Appends a properly formatted known_hosts line for a given hostname, remote address, and public key to any io.Writer. It normalizes addresses and includes both symbolic and IP addresses if they differ. This is useful within an SSH HostKeyCallback to add unknown hosts. ```go import ( "fmt" "log" "net" "os" "github.com/skeema/knownhosts" "golang.org/x/crypto/ssh" ) func main() { khPath := "/home/myuser/.ssh/known_hosts" kh, err := knownhosts.NewDB(khPath) if err != nil { log.Fatal(err) } cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { innerErr := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(innerErr) { return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for %s", hostname) } if knownhosts.IsHostUnknown(innerErr) { 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", hostname) } else { log.Printf("Failed to add %s: %v", hostname, ferr) } return nil // permit unknown host } return innerErr }) config := &ssh.ClientConfig{ User: "myuser", Auth: []ssh.AuthMethod{ssh.Password("secret")}, HostKeyCallback: cb, HostKeyAlgorithms: kh.HostKeyAlgorithms("yourserver.com:22"), } client, err := ssh.Dial("tcp", "yourserver.com:22", config) if err != nil { log.Fatal(err) } defer client.Close() } ``` -------------------------------- ### Normalize Source: https://context7.com/skeema/knownhosts/llms.txt Normalizes a host address to the canonical form used in known_hosts files, including stripping port 22 and correctly bracketing IPv6 addresses. It incorporates a fix for an IPv6 bracketing bug. ```APIDOC ## Normalize ### Description Normalizes a host address to the canonical form used in known_hosts files (strips port 22, brackets IPv6, etc.). Includes a fix for an IPv6 bracketing bug in `golang.org/x/crypto` below v0.42.0 ([golang/go#53463](https://github.com/golang/go/issues/53463)). ### Method `knownhosts.Normalize(addr string) string` ### Parameters - **addr** (string) - The host address to normalize. ### Response - **string** - The normalized host address. ### Request Example ```go import "github.com/skeema/knownhosts" examples := []string{ "yourserver.com:22", // → "yourserver.com" "yourserver.com:2222", // → "[yourserver.com]:2222" "2001:db8::1", // → "2001:db8::1" (IPv6, fixed) "[2001:db8::1]:22", // → "2001:db8::1" } for _, addr := range examples { fmt.Printf("% -30s → %s\n", addr, knownhosts.Normalize(addr)) } ``` ``` -------------------------------- ### Normalize Host Address for known_hosts Source: https://context7.com/skeema/knownhosts/llms.txt Normalizes a host address to the canonical form used in known_hosts files, stripping port 22, bracketing IPv6, etc. Includes a fix for an IPv6 bracketing bug in golang.org/x/crypto below v0.42.0. ```go import "github.com/skeema/knownhosts" examples := []string{ "yourserver.com:22", // → "yourserver.com" "yourserver.com:2222", // → "[yourserver.com]:2222" "2001:db8::1", // → "2001:db8::1" (IPv6, fixed) "[2001:db8::1]:22", // → "2001:db8::1" } for _, addr := range examples { fmt.Printf("% -30s → %s\n", addr, knownhosts.Normalize(addr)) } ``` -------------------------------- ### WriteKnownHostCA Source: https://context7.com/skeema/knownhosts/llms.txt Appends a `@cert-authority` entry to the known_hosts file for a given host pattern and CA public key. This is used for trusting a certificate authority. ```APIDOC ## WriteKnownHostCA ### Description Appends a `@cert-authority` line to any `io.Writer` for a given host pattern and CA public key. Use this when bootstrapping a known_hosts file that trusts a certificate authority rather than individual host keys. ### Method `knownhosts.WriteKnownHostCA(w io.Writer, hostPattern string, caPubKey ssh.PublicKey) error` ### Parameters - **w** (io.Writer) - The writer to append the `@cert-authority` line to. - **hostPattern** (string) - The host pattern to associate with the CA. - **caPubKey** (ssh.PublicKey) - The public key of the certificate authority. ### Request Example ```go import ( "os" "golang.org/x/crypto/ssh" "github.com/skeema/knownhosts" ) func trustCA(hostPattern string, caPubKey ssh.PublicKey) error { f, err := os.OpenFile("/home/myuser/.ssh/known_hosts", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } defer f.Close() // Writes: @cert-authority *.example.com ssh-rsa AAAA... return knownhosts.WriteKnownHostCA(f, hostPattern, caPubKey) } ``` ``` -------------------------------- ### Detect Changed Host Keys (Possible MitM) Source: https://context7.com/skeema/knownhosts/llms.txt Identifies when an `ssh.HostKeyCallback` returns an error indicating a known host's key has changed, which may signal a man-in-the-middle attack. This function wraps `knownhosts.KeyError` checks. ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { // Hard fail: known host presented a different key return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for %s", hostname) } return err }) config := &ssh.ClientConfig{ HostKeyCallback: cb, // ... } _ = config ``` -------------------------------- ### HostKeyDB.HostKeys Source: https://context7.com/skeema/knownhosts/llms.txt Queries known public keys for a specific host and port. It returns a slice of PublicKey values, indicating whether each key is a certificate authority (CA) or a direct host key. ```APIDOC ## HostKeyDB.HostKeys — Query known public keys for a host ### Description Returns a slice of `PublicKey` values (each wrapping `ssh.PublicKey` with an extra `Cert bool` field) for all known_hosts entries that match the given `host:port`. The `Cert` field is `true` when the entry was a `@cert-authority` line (only populated when using `NewDB`). Returns an empty slice if the host is not known. ### Method `HostKeys(host string)` ### Parameters #### Path Parameters - **host** (string) - Required - The host and port to query (e.g., "yourserver.com:22"). ### Response #### Success Response (Slice of PublicKey) - **PublicKey** (struct) - Contains `ssh.PublicKey` and a `Cert bool` field. ### Request Example ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") keys := kh.HostKeys("yourserver.com:22") for _, k := range keys { fmt.Printf("type=%s cert=%v fingerprint=%s\n", k.Type(), k.Cert, ssh.FingerprintSHA256(k), ) } ``` ### Response Example ``` type=ssh-ed25519 cert=false fingerprint=SHA256:abc123... type=ecdsa-sha2-nistp256 cert=false fingerprint=SHA256:def456... type=ssh-rsa cert=true fingerprint=SHA256:ghi789... ``` ``` -------------------------------- ### IsHostKeyChanged Source: https://context7.com/skeema/knownhosts/llms.txt Detects if an SSH host key has changed, which could indicate a man-in-the-middle attack. It checks if the error returned by an `ssh.HostKeyCallback` signifies a known host with a modified key. ```APIDOC ## IsHostKeyChanged — Detect changed host keys (possible MitM) ### Description Returns `true` when an error returned by an `ssh.HostKeyCallback` indicates that the host is **known** but its key has **changed** — a strong signal of a man-in-the-middle attack. Wraps the upstream `knownhosts.KeyError` check. ### Method `IsHostKeyChanged(err error) bool` ### Parameters #### Path Parameters - **err** (error) - Required - The error returned from an `ssh.HostKeyCallback`. ### Response #### Success Response (boolean) - **true** if the host key has changed, **false** otherwise. ### Request Example ```go kh, _ := knownhosts.NewDB("/home/myuser/.ssh/known_hosts") cb := ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { err := kh.HostKeyCallback()(hostname, remote, key) if knownhosts.IsHostKeyChanged(err) { // Hard fail: known host presented a different key return fmt.Errorf("REMOTE HOST IDENTIFICATION HAS CHANGED for %s", hostname) } return err }) config := &ssh.ClientConfig{ HostKeyCallback: cb, // ... } _ = config ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.