diff --git a/design/65269-crypto-ssh-v2.md b/design/65269-crypto-ssh-v2.md new file mode 100644 index 00000000..ea1ac05f --- /dev/null +++ b/design/65269-crypto-ssh-v2.md @@ -0,0 +1,403 @@ +# x/crypto/ssh/v2 + +Author(s): Nicola Murino, Filippo Valsorda + +## Motivation + +We are discussing about [migrating x/crypto packages to the standard library](https://github.com/golang/go/issues/65269). + +The package `x/crypto/ssh` is one of the most used package in x/crypto and so it is stable and works well. +However, over the years we have accumulated several sub-optimal implementations to keep backward compatibility and we have realized that some interfaces are not implemented outside the packages itself and therefore can be removed. + +The ssh server implementation does not have an high-level API similar to net/http `ListenAndServe`. + +Additionally we want to make some API more consistent with the standard library, for example we should rewrite API returning Go channels and remove deprecated API (e.g. DSA support). + +For client and server APIs we want to have both a high-level and a low-level API to provide an easy way to handle the most common use cases, but also to enable our users to handle more advanced use cases by using the low-level API. + +This means that we cannot merge `x/crypto/ssh` as is, the changes described here will lead to a v2. + +### Proposal + +The proposal is to add this new v2 to `x/cryoto/ssh` initially and then move it to the standard library. + +`golang/x/crypto/ssh/v2` will become a wrapper for the package in the standard library once `v2` is merged. + +See [docs](./65269/golang.org/x/crypto/ssh/index.html) for full details and examples. + +### Interfaces removal + +#### Remove Conn interface + +The [Conn](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/connection.go#L50) interface is unlikely to be used outside the `ssh` package. Implementing it means also implementing the ssh connection protocol. We can remove this interface and just use [connection](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/connection.go#L88), its implementation, internally. + +#### Convert ConnMetadata to a struct + +The [ConnMetadata](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/connection.go#L24) interface holds metadata for the connection and after removing the `Conn` interface it can be converted to a struct so we don't have to add an interface extension each time we need to add a new method here. It was previosuly an interface because part of the `Conn` interface. + +#### Convert Channel and NewChannel to structs + +The [Channel](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/channel.go#L49) and [NewChannel](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/channel.go#L28) interfaces can be converted to structs after removing the `Conn` interface. They previously were interface because returned by methods in `Conn` interface. + +### Add a context to the low lever server API + +Our `Server` implementation provide a low level API, [NewServerConn](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/server.go#L205) to create a server connection from a `net.Conn`. This API starts an SSH handshake and can block if the provided `net.Conn` does not have a deadline. We'll update this API by adding a context so it is most clear that it can block. There is an open [proposal](https://github.com/golang/go/issues/66823) for this change. In v2 we can add the context directly without adding the `NewServerConnContext` variant. + +### Add context to high level client API + +Add a context to the high level API to create a client. + +```go +Dial(ctx context.Context, network, addr string, config *ClientConfig) (*Client, error) +``` + +This means we can also remove the `Timeout` field from the [ClientConfig](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/client.go#L241) struct. + +### Rename ServerConfig to Server + +We have a [ServerConfig](https://github.com/golang/crypto/blob/a6a393ffd658b286f64f141b06cbd94e516d3a64/ssh/server.go#L63) struct but not a `Server` struct, the current `ServerConfig` is quit similar to `http.Server` and since we also plan to add some high-level API to our ssh server, see below, it makes sense to rename `ServerConfig` to just `Server`. + +### New common interfaces and handler functions + +SSH package is all around Requests and Channels so we can add some commom interfaces to reuse in our client and server implementation. + +```go +// ChannelHandler defines the interface to handle new channel requests. +type ChannelHandler interface { + NewChannel(ch *NewChannel) +} + +// RequestHandler defines the interface to handle new [Request]. +type RequestHandler interface { + NewRequest(req *Request) +} + +// ChannelHandlerFunc is an adapter to allow the use of ordinary function as +// [ChannelHandler]. If f is a function with the appropriate signature, +// ChannelHandlerFunc(f) is a [ChannelHandler] that calls f. +type ChannelHandlerFunc func(ch *NewChannel) + +// RequestHandlerFunc is an adapter to allow the use of ordinary function as +// [RequestHandler]. If f is a function with the appropriate signature, +// RequestHandlerFunc(f) is a [RequestHandler] that calls f. +type RequestHandlerFunc func(req *Request) +``` + +### High-level Server API + +The SSH server lacks high-level APIs, users should manually handle listening for new connections and creating SSH server connections. This may confuse new users or users coming from `net.http`, we should provide a well-know server pattern like `net.http`. + +The proposal is to add the following high level APIs: + +```go +func (s *Server) Serve(l net.Listener) error +func (s *Server) ListenAndServe(addr string) error +func (s *Server) Close() error +``` + +we also need to add some new fields to the `Server` struct + +```go +type Server struct { + .... + // HandshakeTimeout defines the timeout for the initial handshake, as milliseconds. + HandshakeTimeout int + + // ConnectionFailed, if non-nil, is called to report handshake errors. + ConnectionFailed func(c net.Conn, err error) + + // ConnectionAdded, if non-nil, is called when a client connects, by + // returning an error the connection will be refused. + ConnectionAdded func(c net.Conn) error + + // ClientHandler defines the handler for authenticated clients. It is called + // if the handshake is successfull. The handler must serve requests and + // channels using [ServerConn.Handle]. + ClientHandler ClientHandler +} + +// ClientHandler defines the interface to handle authenticated server +// connections. +type ClientHandler interface { + // HandleClient is called after the handshake completes and a client + // authenticates with the server. + HandleClient(conn *ServerConn) +} + +// ClientHandlerFunc is an adapter to allow the use of ordinary function as +// [ClientHandler]. If f is a function with the appropriate signature, +// ClientHandlerFunc(f) is a [ClientHandler] that calls f. +type ClientHandlerFunc func(conn *ServerConn) +``` + +Usage example for high-level API: + +```go +server := &ssh.Server{ + Password: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + ... + }, + ConnectionFailed: func(c net.Conn, err error) { + ... + }, + ConnectionAdded: func(c net.Conn) error { + ... + }, + ClientHandler: ssh.ClientHandlerFunc(func(conn *ssh.ServerConn) { + conn.Handle( + ssh.ChannelHandlerFunc(func(newChannel *ssh.NewChannel) { + .... + }), + ssh.RequestHandlerFunc(func(req *ssh.Request) { + .... + })) + }), +} + +server.AddHostKey(ed25519Key) + +if err := server.ListenAndServe(":3022"); err != nil { + panic(err) +} +``` + +### Refactor API returning channels + +In the `ssh` package we have several APIs returning Go channels, this is not common in the standard library so we should change some APIs and instead of returning something like `(chans <-chan NewChannel, reqs <-chan *Request)` we'll add an `Handle` method to `ServerConn`, `ClientConn` and `Channel` implementation: + +```go +// Handle must be called to handle requests and channels. Handle blocks. If +// channelHandler is nil channels will be rejected. If requestHandler is nil, +// requests will be discarded. +func (c *ServerConn) Handle(channelHandler ChannelHandler, requestHandler RequestHandler) error + +// Handle must be called to handle requests and channels if you want to handle a +// [ClientConn] yourself without building a [Client] using [NewClient]. Handle +// blocks. If channelHandler is nil channels will be rejected. If requestHandler +// is nil, requests will be discarded. +func (c *ClientConn) Handle(channelHandler ChannelHandler, requestHandler RequestHandler) error + +// Handle must be called to handle channel's requests. Handle blocks. If +// requestHandler is nil, requests will be discarded. +func (c *Channel) Handle(handler RequestHandler) error +``` + +We can also remove the `DiscardRequests` package level method because channels and requests are now automatically discarded if a nil handler is passed to the `Handle` methods. + +### Remove Callback suffix from Client and Server configs + +Client and Server configs have fields like these: + +```go +PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) + +BannerCallback BannerCallback +``` + +Adding the `Callback` suffix is quite unusal in the standard library, we should remove this suffix like this: + +```go +Password func(conn ConnMetadata, password []byte) (*Permissions, error) + +Banner BannerCallback +``` + +## Remove NewSignerFromKey, rename NewSignerFromSigner to NewSigner + +Currently we have the following APIs to create a `Signer`. + +```go +// NewSignerFromSigner takes any crypto.Signer implementation and +// returns a corresponding Signer interface. This can be used, for +// example, with keys kept in hardware modules. +func NewSignerFromSigner(signer crypto.Signer) (Signer, error) + +// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, +// *ecdsa.PrivateKey or any other crypto.Signer and returns a +// corresponding Signer instance. ECDSA keys must use P-256, P-384 or +// P-521. DSA keys must use parameter size L1024N160. +func NewSignerFromKey(key interface{}) (Signer, error) +``` + +`NewSignerFromKey` is required to handle `dsa.PrivateKey`, since DSA will be removed, we can also remove `NewSignerFromKey` and rename `NewSignerFromSigner` in `NewSigner`. + +```go +// NewSigner takes any crypto.Signer implementation and returns a corresponding +// Signer interface. This can be used, for example, with keys kept in hardware +// modules. +func NewSigner(signer crypto.Signer) (Signer, error) +``` + +### Extend Signer interface + +Initially the `Signer` interface was very simple and did not allow to specify the algorithm to use for signing or to list the supported signing algorithms, so to maintain backward compatibility we extended it. + +```go +// A Signer can create signatures that verify against a public key. +// +// Some Signers provided by this package also implement MultiAlgorithmSigner. +type Signer interface { + // PublicKey returns the associated PublicKey. + PublicKey() PublicKey + + // Sign returns a signature for the given data. This method will hash the + // data appropriately first. The signature algorithm is expected to match + // the key format returned by the PublicKey.Type method (and not to be any + // alternative algorithm supported by the key format). + Sign(rand io.Reader, data []byte) (*Signature, error) +} + +// An AlgorithmSigner is a Signer that also supports specifying an algorithm to +// use for signing. +// +// An AlgorithmSigner can't advertise the algorithms it supports, unless it also +// implements MultiAlgorithmSigner, so it should be prepared to be invoked with +// every algorithm supported by the public key format. +type AlgorithmSigner interface { + Signer + + // SignWithAlgorithm is like Signer.Sign, but allows specifying a desired + // signing algorithm. Callers may pass an empty string for the algorithm in + // which case the AlgorithmSigner will use a default algorithm. This default + // doesn't currently control any behavior in this package. + SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) +} + +// MultiAlgorithmSigner is an AlgorithmSigner that also reports the algorithms +// supported by that signer. +type MultiAlgorithmSigner interface { + AlgorithmSigner + + // Algorithms returns the available algorithms in preference order. The list + // must not be empty, and it must not include certificate types. + Algorithms() []string +} +``` + +Extending existing implementations to add these additional methods is quite simple so we may evaluate to change the interface in v2. + +```go +// A Signer can create signatures that verify against a public key. +// +// Some Signers provided by this package also implement MultiAlgorithmSigner. +type Signer interface { + // PublicKey returns the associated PublicKey. + PublicKey() PublicKey + + // Sign returns a signature for the given data. This method will hash the + // data appropriately first. The signature algorithm is expected to match + // the key format returned by the PublicKey.Type method (and not to be any + // alternative algorithm supported by the key format). + Sign(rand io.Reader, data []byte) (*Signature, error) + + // SignWithAlgorithm is like Signer.Sign, but allows specifying a desired + // signing algorithm. Callers may pass an empty string for the algorithm in + // which case the AlgorithmSigner will use a default algorithm. This default + // doesn't currently control any behavior in this package. + SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) +  + // Algorithms returns the available algorithms in preference order. The list + // must not be empty, and it must not include certificate types. + Algorithms() []string +} +``` + +Suppose you have a signer implementation supporting a single algorithm like this. + +```go +type mySigner struct {} + +func (s *mySigner) Type() string + +func (s *mySigner) Marshal() []byte + +func (s *mySigner) Verify(data []byte, sig *Signature) error + +func (s *mySigner) Sign(rand io.Reader, data []byte) (*Signature, error) +``` + +To implement the new `Signer` interface, you have to add the `Algorithms() []string` method that return the supported algorithm and the `SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)` that just check that the specified algporithm is valid and then call the existing `Sign` method. + +### Agent + +Remove the method `SignWithFlags` from `ExtendedAgent` so that the `ExtendedAgent` interface only handle extensions. +We keep both `Sign` and `SignWithFlags` so that `Sign` can also be used to implement the `ssh.Signer` interface. + +```go +// Agent represents the capabilities of an ssh-agent. +type Agent interface { + // List returns the identities known to the agent. + List() ([]*Key, error) + + // Sign has the agent sign the data using a protocol 2 key as defined + // in [PROTOCOL.agent] section 2.6.2. + Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) + + // SignWithFlags signs like Sign, but allows for additional flags to be sent/received. + SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) + + // Add adds a private key to the agent. + Add(key AddedKey) error + + // Remove removes all identities with the given public key. + Remove(key ssh.PublicKey) error + + // RemoveAll removes all identities. + RemoveAll() error + + // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. + Lock(passphrase []byte) error + + // Unlock undoes the effect of Lock + Unlock(passphrase []byte) error + + // Signers returns signers for all the known keys. + Signers() ([]ssh.Signer, error) +} + +type ExtendedAgent interface { + Agent + + // Extension processes a custom extension request. Standard-compliant agents are not + // required to support any extensions, but this method allows agents to implement + // vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7. + // If agent extensions are unsupported entirely this method MUST return an + // ErrExtensionUnsupported error. Similarly, if just the specific extensionType in + // the request is unsupported by the agent then ErrExtensionUnsupported MUST be + // returned. + // + // In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents + // of the response are unspecified (including the type of the message), the complete + // response will be returned as a []byte slice, including the "type" byte of the message. + Extension(extensionType string, contents []byte) ([]byte, error) +} +``` + +### Deprecated API and algorithms removal + +We'll remove DSA support, see [here](https://lists.mindrot.org/pipermail/openssh-unix-announce/2024-January/000156.html) for DSA status in OpenSSH, it is already disabled by default and will be removed in January, 2025. + +The following deprecated constants will be removed. + +```go +const ( + // Deprecated: use CertAlgoRSAv01. + CertSigAlgoRSAv01 = CertAlgoRSAv01 + // Deprecated: use CertAlgoRSASHA256v01. + CertSigAlgoRSASHA2256v01 = CertAlgoRSASHA256v01 + // Deprecated: use CertAlgoRSASHA512v01. + CertSigAlgoRSASHA2512v01 = CertAlgoRSASHA512v01 +) + +const ( + // Deprecated: use KeyAlgoRSA. + SigAlgoRSA = KeyAlgoRSA + // Deprecated: use KeyAlgoRSASHA256. + SigAlgoRSASHA2256 = KeyAlgoRSASHA256 + // Deprecated: use KeyAlgoRSASHA512. + SigAlgoRSASHA2512 = KeyAlgoRSASHA512 +) +``` + +The `terminal` package is deprecated and will be removed. +The `test` and `testdata` packages will be moved to `internal`. diff --git a/design/65269/_/css/main.css b/design/65269/_/css/main.css new file mode 100644 index 00000000..aad59947 --- /dev/null +++ b/design/65269/_/css/main.css @@ -0,0 +1,161 @@ +body { + margin: 1em 2em; + font-family: Helvetica, sans-serif; + background-color: #f8f8f8; + font-size: 1em; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0.3em; + margin-bottom: 0.3em; +} +h1, h2, h3, h4 { font-weight: 500; } +h2 { font-size: 1.75em } +h3 { font-size: 1.5em } +h4 { font-size: 1.33em } +h5 { font-size: 1em } + +a { + text-decoration: none; + color: #0366a5; +} +a:hover { + text-decoration: underline; +} + +a.permalink { display: none; } +a.permalink:hover { + text-decoration: none; +} +*:hover > a.permalink { display: inline; } + +nav { + padding: 1em; + background-color: #eee; + border-radius: 0.5em; + display: flex; + flex-wrap: wrap; +} + +nav .navbar-right { + margin-left: auto; +} + +/* Remove first level of nesting for a package's index section. */ +#pkg-index + ul, #pkg-examples + ul { + list-style-type: none; + padding: 0; +} + +code, kbd, pre { + font-family: Consolas, monospace; +} + +pre { + color: #222; + overflow-x: auto; + border: 1px solid #ccc; + border-radius: 0.5em; + background-color: #eee; + padding: 0.75em; + font-size: 0.9em; +} + +details.example > summary { + color: #0366a5; + cursor: pointer; +} + +details.deprecated > summary { + list-style: none; +} + +span.deprecated-tag { + color: #eee; + background-color: #999; + padding: 0.125rem 0.3rem; + border-radius: 0.3rem; + font-size: 0.7rem; + vertical-align: middle; + cursor: pointer; +} + +#search { margin: 0.3em 0; } + +#generated-by-footer { font-size: x-small; } + +/* Background */ .bg { background-color: #ffffff; } +/* PreWrapper */ .chroma { background-color: #ffffff; } +/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } +/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .chroma .hl { background-color: #e5e5e5 } +/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { color: #000000; font-weight: bold } +/* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold } +/* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold } +/* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold } +/* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold } +/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold } +/* NameAttribute */ .chroma .na { color: #008080 } +/* NameBuiltin */ .chroma .nb { color: #0086b3 } +/* NameBuiltinPseudo */ .chroma .bp { color: #999999 } +/* NameClass */ .chroma .nc { color: #445588; font-weight: bold } +/* NameConstant */ .chroma .no { color: #008080 } +/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold } +/* NameEntity */ .chroma .ni { color: #800080 } +/* NameException */ .chroma .ne { color: #990000; font-weight: bold } +/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold } +/* NameLabel */ .chroma .nl { color: #990000; font-weight: bold } +/* NameNamespace */ .chroma .nn { color: #555555 } +/* NameTag */ .chroma .nt { color: #000080 } +/* NameVariable */ .chroma .nv { color: #008080 } +/* NameVariableClass */ .chroma .vc { color: #008080 } +/* NameVariableGlobal */ .chroma .vg { color: #008080 } +/* NameVariableInstance */ .chroma .vi { color: #008080 } +/* LiteralString */ .chroma .s { color: #dd1144 } +/* LiteralStringAffix */ .chroma .sa { color: #dd1144 } +/* LiteralStringBacktick */ .chroma .sb { color: #dd1144 } +/* LiteralStringChar */ .chroma .sc { color: #dd1144 } +/* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 } +/* LiteralStringDoc */ .chroma .sd { color: #dd1144 } +/* LiteralStringDouble */ .chroma .s2 { color: #dd1144 } +/* LiteralStringEscape */ .chroma .se { color: #dd1144 } +/* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 } +/* LiteralStringInterpol */ .chroma .si { color: #dd1144 } +/* LiteralStringOther */ .chroma .sx { color: #dd1144 } +/* LiteralStringRegex */ .chroma .sr { color: #009926 } +/* LiteralStringSingle */ .chroma .s1 { color: #dd1144 } +/* LiteralStringSymbol */ .chroma .ss { color: #990073 } +/* LiteralNumber */ .chroma .m { color: #009999 } +/* LiteralNumberBin */ .chroma .mb { color: #009999 } +/* LiteralNumberFloat */ .chroma .mf { color: #009999 } +/* LiteralNumberHex */ .chroma .mh { color: #009999 } +/* LiteralNumberInteger */ .chroma .mi { color: #009999 } +/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 } +/* LiteralNumberOct */ .chroma .mo { color: #009999 } +/* Operator */ .chroma .o { color: #000000; font-weight: bold } +/* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold } +/* Comment */ .chroma .c { color: #999988; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic } +/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic } +/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd } +/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic } +/* GenericError */ .chroma .gr { color: #aa0000 } +/* GenericHeading */ .chroma .gh { color: #999999 } +/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd } +/* GenericOutput */ .chroma .go { color: #888888 } +/* GenericPrompt */ .chroma .gp { color: #555555 } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #aaaaaa } +/* GenericTraceback */ .chroma .gt { color: #aa0000 } +/* GenericUnderline */ .chroma .gl { text-decoration: underline } +/* TextWhitespace */ .chroma .w { color: #bbbbbb } diff --git a/design/65269/_/icons/apple-touch-icon.png b/design/65269/_/icons/apple-touch-icon.png new file mode 100644 index 00000000..8b847112 Binary files /dev/null and b/design/65269/_/icons/apple-touch-icon.png differ diff --git a/design/65269/_/icons/favicon-16x16.png b/design/65269/_/icons/favicon-16x16.png new file mode 100644 index 00000000..445198b0 Binary files /dev/null and b/design/65269/_/icons/favicon-16x16.png differ diff --git a/design/65269/_/icons/favicon-32x32.png b/design/65269/_/icons/favicon-32x32.png new file mode 100644 index 00000000..393810fb Binary files /dev/null and b/design/65269/_/icons/favicon-32x32.png differ diff --git a/design/65269/_/icons/favicon.ico b/design/65269/_/icons/favicon.ico new file mode 100644 index 00000000..e7452a5f Binary files /dev/null and b/design/65269/_/icons/favicon.ico differ diff --git a/design/65269/_/js/permalink.js b/design/65269/_/js/permalink.js new file mode 100644 index 00000000..062ccf3f --- /dev/null +++ b/design/65269/_/js/permalink.js @@ -0,0 +1,44 @@ +// If the page was opened with an anchor (e.g. #foo), +// and the destination is a
element, open it. +function openDetailsAnchor() { + let hash = window.location.hash + if (!hash) { + return + } + let el = document.getElementById(hash.slice(1)) // remove leading '#' + if (!el) { + return + } + + let details = el.closest("details") + while (details) { + details.open = true + details = details.parentElement.closest("details") + } + + // New elements may have appeared. + // Set hash again to scroll to the right place. + window.location.hash = hash; + return false; +} + +window.addEventListener('hashchange', openDetailsAnchor) + +window.addEventListener('load', () => { + document.querySelectorAll("h2, h3, h4, h5, h6").forEach((el) => { + if (!el.id) { + return + } + el.innerHTML += ' ' + }) + + document.querySelectorAll("details.example > summary").forEach((el) => { + let id = el.parentElement.id; + if (!id) { + return + } + el.innerHTML += ' ' + }) + + openDetailsAnchor() +}) diff --git a/design/65269/golang.org/index.html b/design/65269/golang.org/index.html new file mode 100644 index 00000000..73c98f23 --- /dev/null +++ b/design/65269/golang.org/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + + golang.org + + + +

Directories

+ + + + + + + + +
x/crypto/sshPackage ssh implements an SSH client and server.
+
+
+ + + diff --git a/design/65269/golang.org/x/crypto/index.html b/design/65269/golang.org/x/crypto/index.html new file mode 100644 index 00000000..658fda2d --- /dev/null +++ b/design/65269/golang.org/x/crypto/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + + golang.org/x/crypto + + + +

Directories

+ + + + + + + + +
sshPackage ssh implements an SSH client and server.
+
+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/agent/index.html b/design/65269/golang.org/x/crypto/ssh/agent/index.html new file mode 100644 index 00000000..abc2ea10 --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/agent/index.html @@ -0,0 +1,263 @@ + + + + + + + + + + + agent + + + +

package agent

+
import "golang.org/x/crypto/ssh/agent"
+

Package agent implements the ssh-agent protocol, and provides both +a client and a server. The client can talk to a standard ssh-agent +that uses UNIX sockets, and one could implement an alternative +ssh-agent process using the sample server. +

References: +

[PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-14
+
+

Index

+

Examples

+

Variables

+
var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
+

ErrExtensionUnsupported indicates that an extension defined in +[PROTOCOL.agent] section 3.8 is unsupported by the agent. Specifically this +error indicates that the agent returned a standard SSH_AGENT_FAILURE message +as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol +specification (and therefore this error) does not distinguish between a +specific extension being unsupported and extensions being unsupported entirely. +

Functions

+

func ForwardToAgent

+
func ForwardToAgent(client *ssh.Client, keyring Agent) error
+

ForwardToAgent routes authentication requests to the given keyring. +

func ForwardToRemote

+
func ForwardToRemote(client *ssh.Client, addr string) error
+

ForwardToRemote routes authentication requests to the ssh-agent +process serving on the given unix socket. +

func RequestAgentForwarding

+
func RequestAgentForwarding(session *ssh.Session) error
+

RequestAgentForwarding sets up agent forwarding for the session. +ForwardToAgent or ForwardToRemote should be called to route +the authentication requests. +

func ServeAgent

+
func ServeAgent(agent Agent, c io.ReadWriter) error
+

ServeAgent serves the agent protocol on the given connection. It +returns when an I/O error occurs. +

Types

+

type AddedKey

+
type AddedKey struct {
+	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey,
+	// ed25519.PrivateKey or *ecdsa.PrivateKey, which will be inserted into the
+	// agent.
+	PrivateKey crypto.Signer
+	// Certificate, if not nil, is communicated to the agent and will be
+	// stored with the key.
+	Certificate *ssh.Certificate
+	// Comment is an optional, free-form string.
+	Comment string
+	// LifetimeSecs, if not zero, is the number of seconds that the
+	// agent will store the key for.
+	LifetimeSecs uint32
+	// ConfirmBeforeUse, if true, requests that the agent confirm with the
+	// user before each use of this key.
+	ConfirmBeforeUse bool
+	// ConstraintExtensions are the experimental or private-use constraints
+	// defined by users.
+	ConstraintExtensions []ConstraintExtension
+}
+

AddedKey describes an SSH key to be added to an Agent. +

type Agent

+
type Agent interface {
+	// List returns the identities known to the agent.
+	List() ([]*Key, error)
+
+	// Sign has the agent sign the data using a protocol 2 key as defined in
+	// [PROTOCOL.agent] section 3.6. This method is also implemented in
+	// [ssh.Signer] interface.
+	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
+
+	// SignWithFlags signs like Sign, but allows for additional flags to be
+	// sent/received. See [PROTOCOL.agent] section 3.6.1.
+	SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
+
+	// Add adds a private key to the agent.
+	Add(key AddedKey) error
+
+	// Remove removes all identities with the given public key.
+	Remove(key ssh.PublicKey) error
+
+	// RemoveAll removes all identities.
+	RemoveAll() error
+
+	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
+	Lock(passphrase []byte) error
+
+	// Unlock undoes the effect of Lock
+	Unlock(passphrase []byte) error
+
+	// Signers returns signers for all the known keys.
+	Signers() ([]ssh.Signer, error)
+}
+

Agent represents the capabilities of an ssh-agent. +

func NewKeyring

+
func NewKeyring() Agent
+

NewKeyring returns an Agent that holds keys in memory. It is safe +for concurrent use by multiple goroutines. +

type ConstraintExtension

+
type ConstraintExtension struct {
+	// ExtensionName consist of a UTF-8 string suffixed by the
+	// implementation domain following the naming scheme defined
+	// in Section 4.2 of RFC 4251, e.g.  "foo@example.com".
+	ExtensionName string
+	// ExtensionDetails contains the actual content of the extended
+	// constraint.
+	ExtensionDetails []byte
+}
+

ConstraintExtension describes an optional constraint defined by users. +

type ExtendedAgent

+
type ExtendedAgent interface {
+	Agent
+
+	// Extension processes a custom extension request. Standard-compliant agents are not
+	// required to support any extensions, but this method allows agents to implement
+	// vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 3.8.
+	// If agent extensions are unsupported entirely this method MUST return an
+	// ErrExtensionUnsupported error. Similarly, if just the specific extensionType in
+	// the request is unsupported by the agent then ErrExtensionUnsupported MUST be
+	// returned.
+	//
+	// In the case of success, since [PROTOCOL.agent] section 3.8 specifies that the contents
+	// of the response are unspecified (including the type of the message), the complete
+	// response will be returned as a []byte slice, including the "type" byte of the message.
+	Extension(extensionType string, contents []byte) ([]byte, error)
+}
+

func NewClient

+
func NewClient(rw io.ReadWriter) ExtendedAgent
+

NewClient returns an Agent that talks to an ssh-agent process over +the given connection. +

+ Example +
package main
+
+import (
+	"context"
+	"log"
+	"net"
+	"os"
+
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/crypto/ssh/agent"
+)
+
+func main() {
+	// ssh-agent(1) provides a UNIX socket at $SSH_AUTH_SOCK.
+	socket := os.Getenv("SSH_AUTH_SOCK")
+	conn, err := net.Dial("unix", socket)
+	if err != nil {
+		log.Fatalf("Failed to open SSH_AUTH_SOCK: %v", err)
+	}
+
+	agentClient := agent.NewClient(conn)
+	config := &ssh.ClientConfig{
+		User: "gopher",
+		Auth: []ssh.AuthMethod{
+			// Use a callback rather than PublicKeys so we only consult the
+			// agent once the remote server wants it.
+			ssh.PublicKeysCallback(agentClient.Signers),
+		},
+		HostKey: ssh.InsecureIgnoreHostKey(),
+	}
+
+	sshc, err := ssh.Dial(context.Background(), "tcp", "localhost:22", config)
+	if err != nil {
+		log.Fatal(err)
+	}
+	// Use sshc...
+	sshc.Close()
+}
+
+

type Key

+
type Key struct {
+	Format  string
+	Blob    []byte
+	Comment string
+}
+

Key represents a protocol 2 public key as defined in +[PROTOCOL.agent], section 3.3. +

func (*Key) Marshal

+
func (k *Key) Marshal() []byte
+

Marshal returns key blob to satisfy the ssh.PublicKey interface. +

func (*Key) String

+
func (k *Key) String() string
+

String returns the storage form of an agent key with the format, base64 +encoded serialized key, and the comment if it is not empty. +

func (*Key) Type

+
func (k *Key) Type() string
+

Type returns the public key type. +

func (*Key) Verify

+
func (k *Key) Verify(data []byte, sig *ssh.Signature) error
+

Verify satisfies the ssh.PublicKey interface. +

type SignatureFlags

+
type SignatureFlags uint32
+

SignatureFlags represent additional flags that can be passed to the signature +requests an defined in [PROTOCOL.agent] section 3.6.1. +

const (
+	SignatureFlagReserved SignatureFlags = 1 << iota
+	SignatureFlagRsaSha256
+	SignatureFlagRsaSha512
+)
+

SignatureFlag values as defined in [PROTOCOL.agent] section 5.3. +

+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/index.html b/design/65269/golang.org/x/crypto/ssh/index.html new file mode 100644 index 00000000..33a242fb --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/index.html @@ -0,0 +1,2234 @@ + + + + + + + + + + + ssh + + + +

package ssh

+
import "golang.org/x/crypto/ssh"
+

Package ssh implements an SSH client and server. +

SSH is a transport security protocol, an authentication protocol and a +family of application protocols. The most typical application level +protocol is a remote shell and this is specifically implemented. However, +the multiplexed nature of SSH is exposed to users that wish to support +others. +

References: +

[PROTOCOL]: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD
+[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
+[SSH-PARAMETERS]:    http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
+
+

This package does not fall under the stability promise of the Go language itself, +so its API may be changed when pressing needs arise. +

Index

+

Examples

+

Constants

+
const (
+	CertAlgoRSAv01        = "ssh-rsa-cert-v01@openssh.com"
+	CertAlgoECDSA256v01   = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+	CertAlgoECDSA384v01   = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+	CertAlgoECDSA521v01   = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+	CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
+	CertAlgoED25519v01    = "ssh-ed25519-cert-v01@openssh.com"
+	CertAlgoSKED25519v01  = "sk-ssh-ed25519-cert-v01@openssh.com"
+
+	// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
+	// Certificate.Type (or PublicKey.Type), but only in
+	// ClientConfig.HostKeyAlgorithms.
+	CertAlgoRSASHA256v01 = "rsa-sha2-256-cert-v01@openssh.com"
+	CertAlgoRSASHA512v01 = "rsa-sha2-512-cert-v01@openssh.com"
+)
+

Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear +in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms. +Unlike key algorithm names, these are not passed to AlgorithmSigner nor +returned by MultiAlgorithmSigner and don't appear in the Signature.Format +field. +

const (
+	UserCert = 1
+	HostCert = 2
+)
+

Certificate types distinguish between host and user +certificates. The values can be set in the CertType field of +Certificate. +

const (
+	CipherAES128GCM            = "aes128-gcm@openssh.com"
+	CipherAES256GCM            = "aes256-gcm@openssh.com"
+	CipherChacha20Poly1305     = "chacha20-poly1305@openssh.com"
+	CipherAES128CTR            = "aes128-ctr"
+	CipherAES192CTR            = "aes192-ctr"
+	CipherAES256CTR            = "aes256-ctr"
+	InsecureCipherAES128CBC    = "aes128-cbc"
+	InsecureCipherTripleDESCBC = "3des-cbc"
+	InsecureCipherRC4          = "arcfour"
+	InsecureCipherRC4128       = "arcfour128"
+	InsecureCipherRC4256       = "arcfour256"
+)
+

Implemented ciphers algorithms. +

const (
+	InsecureKeyExchangeDH1SHA1   = "diffie-hellman-group1-sha1"
+	InsecureKeyExchangeDH14SHA1  = "diffie-hellman-group14-sha1"
+	KeyExchangeDH14SHA256        = "diffie-hellman-group14-sha256"
+	KeyExchangeDH16SHA512        = "diffie-hellman-group16-sha512"
+	KeyExchangeECDHP256          = "ecdh-sha2-nistp256"
+	KeyExchangeECDHP384          = "ecdh-sha2-nistp384"
+	KeyExchangeECDHP521          = "ecdh-sha2-nistp521"
+	KeyExchangeCurve25519SHA256  = "curve25519-sha256"
+	InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
+	KeyExchangeDHGEXSHA256       = "diffie-hellman-group-exchange-sha256"
+)
+

Implemented key exchanges algorithms. +

const (
+	HMACSHA256ETM      = "hmac-sha2-256-etm@openssh.com"
+	HMACSHA512ETM      = "hmac-sha2-512-etm@openssh.com"
+	HMACSHA256         = "hmac-sha2-256"
+	HMACSHA512         = "hmac-sha2-512"
+	InsecureHMACSHA1   = "hmac-sha1"
+	InsecureHMACSHA196 = "hmac-sha1-96"
+)
+

Implemented message authentication code (MAC) algorithms. +

const (
+	KeyTypeRSA        = "ssh-rsa"
+	KeyTypeECDSA256   = "ecdsa-sha2-nistp256"
+	KeyTypeECDSA384   = "ecdsa-sha2-nistp384"
+	KeyTypeECDSA521   = "ecdsa-sha2-nistp521"
+	KeyTypeSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
+	KeyTypeED25519    = "ssh-ed25519"
+	KeyTypeSKED25519  = "sk-ssh-ed25519@openssh.com"
+)
+

Implemented public key types. +

const (
+	KeyAlgoRSA        = "ssh-rsa"
+	KeyAlgoECDSA256   = "ecdsa-sha2-nistp256"
+	KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
+	KeyAlgoECDSA384   = "ecdsa-sha2-nistp384"
+	KeyAlgoECDSA521   = "ecdsa-sha2-nistp521"
+	KeyAlgoED25519    = "ssh-ed25519"
+	KeyAlgoSKED25519  = "sk-ssh-ed25519@openssh.com"
+
+	// KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not
+	// public key formats, so they can't appear as a PublicKey.Type. The
+	// corresponding PublicKey.Type is KeyAlgoRSA. See RFC 8332, Section 2.
+	KeyAlgoRSASHA256 = "rsa-sha2-256"
+	KeyAlgoRSASHA512 = "rsa-sha2-512"
+)
+

Public key algorithms names. These values can appear in PublicKey.Type, +ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner +arguments. +

const (
+	VINTR         = 1
+	VQUIT         = 2
+	VERASE        = 3
+	VKILL         = 4
+	VEOF          = 5
+	VEOL          = 6
+	VEOL2         = 7
+	VSTART        = 8
+	VSTOP         = 9
+	VSUSP         = 10
+	VDSUSP        = 11
+	VREPRINT      = 12
+	VWERASE       = 13
+	VLNEXT        = 14
+	VFLUSH        = 15
+	VSWTCH        = 16
+	VSTATUS       = 17
+	VDISCARD      = 18
+	IGNPAR        = 30
+	PARMRK        = 31
+	INPCK         = 32
+	ISTRIP        = 33
+	INLCR         = 34
+	IGNCR         = 35
+	ICRNL         = 36
+	IUCLC         = 37
+	IXON          = 38
+	IXANY         = 39
+	IXOFF         = 40
+	IMAXBEL       = 41
+	IUTF8         = 42 // RFC 8160
+	ISIG          = 50
+	ICANON        = 51
+	XCASE         = 52
+	ECHO          = 53
+	ECHOE         = 54
+	ECHOK         = 55
+	ECHONL        = 56
+	NOFLSH        = 57
+	TOSTOP        = 58
+	IEXTEN        = 59
+	ECHOCTL       = 60
+	ECHOKE        = 61
+	PENDIN        = 62
+	OPOST         = 70
+	OLCUC         = 71
+	ONLCR         = 72
+	OCRNL         = 73
+	ONOCR         = 74
+	ONLRET        = 75
+	CS7           = 90
+	CS8           = 91
+	PARENB        = 92
+	PARODD        = 93
+	TTY_OP_ISPEED = 128
+	TTY_OP_OSPEED = 129
+)
+

POSIX terminal mode flags as listed in RFC 4254 Section 8. +

const CertTimeInfinity = 1<<64 - 1
+

CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that +a certificate does not expire. +

Variables

+
var ErrNoAuth = errors.New("ssh: no auth passed yet")
+

ErrNoAuth is the error value returned if no +authentication method has been passed yet. This happens as a normal +part of the authentication loop, since the client first tries +'none' authentication to discover available methods. +It is returned in ServerAuthError.Errors from NewServerConn. +

Functions

+

func FingerprintLegacyMD5

+
func FingerprintLegacyMD5(pubKey PublicKey) string
+

FingerprintLegacyMD5 returns the user presentation of the key's +fingerprint as described by RFC 4716 section 4. +

func FingerprintSHA256

+
func FingerprintSHA256(pubKey PublicKey) string
+

FingerprintSHA256 returns the user presentation of the key's +fingerprint as unpadded base64 encoded sha256 hash. +This format was introduced from OpenSSH 6.8. +https://www.openssh.com/txt/release-6.8 +https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding) +

func Marshal

+
func Marshal(msg interface{}) []byte
+

Marshal serializes the message in msg to SSH wire format. The msg +argument should be a struct or pointer to struct. If the first +member has the "sshtype" tag set to a number in decimal, that +number is prepended to the result. If the last of member has the +"ssh" tag set to "rest", its contents are appended to the output. +

func MarshalAuthorizedKey

+
func MarshalAuthorizedKey(key PublicKey) []byte
+

MarshalAuthorizedKey serializes key for inclusion in an OpenSSH +authorized_keys file. The return value ends with newline. +

func MarshalPrivateKey

+
func MarshalPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error)
+

MarshalPrivateKey returns a PEM block with the private key serialized in the +OpenSSH format. +

func MarshalPrivateKeyWithPassphrase

+
func MarshalPrivateKeyWithPassphrase(key crypto.PrivateKey, comment string, passphrase []byte) (*pem.Block, error)
+

MarshalPrivateKeyWithPassphrase returns a PEM block holding the encrypted +private key serialized in the OpenSSH format. +

func ParseRawPrivateKey

+
func ParseRawPrivateKey(pemBytes []byte) (crypto.Signer, error)
+

ParseRawPrivateKey returns a private key from a PEM encoded private key. It supports +RSA, ECDSA, and Ed25519 private keys in PKCS#1, PKCS#8, OpenSSL, and OpenSSH +formats. If the private key is encrypted, it will return a PassphraseMissingError. +

func ParseRawPrivateKeyWithPassphrase

+
func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (crypto.Signer, error)
+

ParseRawPrivateKeyWithPassphrase returns a private key decrypted with +passphrase from a PEM encoded private key. If the passphrase is wrong, it +will return x509.IncorrectPasswordError. +

func Unmarshal

+
func Unmarshal(data []byte, out interface{}) error
+

Unmarshal parses data in SSH wire format into a structure. The out +argument should be a pointer to struct. If the first member of the +struct has the "sshtype" tag set to a '|'-separated set of numbers +in decimal, the packet must start with one of those numbers. In +case of error, Unmarshal returns a ParseError or +UnexpectedMessageError. +

Types

+

type Algorithms

+
type Algorithms struct {
+	KeyExchanges   []string
+	Ciphers        []string
+	MACs           []string
+	HostKeys       []string
+	PublicKeyAuths []string
+}
+

Algorithms defines a set of algorithms that can be configured in the client +or server config for negotiation during a handshake. +

func InsecureAlgorithms

+
func InsecureAlgorithms() Algorithms
+

InsecureAlgorithms returns algorithms currently implemented by this package +and which have security issues. +

func SupportedAlgorithms

+
func SupportedAlgorithms() Algorithms
+

SupportedAlgorithms returns algorithms currently implemented by this package, +excluding those with security issues, which are returned by +InsecureAlgorithms. The algorithms listed here are in preference order. +

type AuthMethod

+
type AuthMethod interface {
+	// contains filtered or unexported methods
+}
+

An AuthMethod represents an instance of an RFC 4252 authentication method. +

func GSSAPIWithMICAuthMethod

+
func GSSAPIWithMICAuthMethod(gssAPIClient GSSAPIClient, target string) AuthMethod
+

GSSAPIWithMICAuthMethod is an AuthMethod with "gssapi-with-mic" authentication. +See RFC 4462 section 3 +gssAPIClient is implementation of the GSSAPIClient interface, see the definition of the interface for details. +target is the server host you want to log in to. +

func KeyboardInteractive

+
func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod
+

KeyboardInteractive returns an AuthMethod using a prompt/response +sequence controlled by the server. +

func Password

+
func Password(secret string) AuthMethod
+

Password returns an AuthMethod using the given password. +

func PasswordCallback

+
func PasswordCallback(prompt func() (secret string, err error)) AuthMethod
+

PasswordCallback returns an AuthMethod that uses a callback for +fetching a password. +

func PublicKeys

+
func PublicKeys(signers ...Signer) AuthMethod
+

PublicKeys returns an AuthMethod that uses the given key +pairs. +

+ Example +
package main
+
+import (
+	"context"
+	"log"
+	"os"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	var hostKey ssh.PublicKey
+	// A public key may be used to authenticate against the remote
+	// server by using an unencrypted PEM-encoded private key file.
+	//
+	// If you have an encrypted private key, the crypto/x509 package
+	// can be used to decrypt it.
+	key, err := os.ReadFile("/home/user/.ssh/id_rsa")
+	if err != nil {
+		log.Fatalf("unable to read private key: %v", err)
+	}
+
+	// Create the Signer for this private key.
+	signer, err := ssh.ParsePrivateKey(key)
+	if err != nil {
+		log.Fatalf("unable to parse private key: %v", err)
+	}
+
+	config := &ssh.ClientConfig{
+		User: "user",
+		Auth: []ssh.AuthMethod{
+			// Use the PublicKeys method for remote authentication.
+			ssh.PublicKeys(signer),
+		},
+		HostKey: ssh.FixedHostKey(hostKey),
+	}
+
+	// Connect to the remote server and perform the SSH handshake.
+	client, err := ssh.Dial(context.Background(), "tcp", "host.com:22", config)
+	if err != nil {
+		log.Fatalf("unable to connect: %v", err)
+	}
+	defer client.Close()
+}
+
+

func PublicKeysCallback

+
func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMethod
+

PublicKeysCallback returns an AuthMethod that runs the given +function to obtain a list of key pairs. +

func RetryableAuthMethod

+
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod
+

RetryableAuthMethod is a decorator for other auth methods enabling them to +be retried up to maxTries before considering that AuthMethod itself failed. +If maxTries is <= 0, will retry indefinitely +

This is useful for interactive clients using challenge/response type +authentication (e.g. Keyboard-Interactive, Password, etc) where the user +could mistype their response resulting in the server issuing a +SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4 +[keyboard-interactive]); Without this decorator, the non-retryable +AuthMethod would be removed from future consideration, and never tried again +(and so the user would never be able to retry their entry). +

+ Example +
user := "testuser"
+NumberOfPrompts := 3
+
+// Normally this would be a callback that prompts the user to answer the
+// provided questions
+Cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
+	return []string{"answer1", "answer2"}, nil
+}
+
+config := &ClientConfig{
+	HostKey: InsecureIgnoreHostKey(),
+	User:    user,
+	Auth: []AuthMethod{
+		RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts),
+	},
+}
+
+host := "mysshserver"
+netConn, err := net.Dial("tcp", host)
+if err != nil {
+	log.Fatal(err)
+}
+
+sshConn, err := NewClientConn(netConn, host, config)
+if err != nil {
+	log.Fatal(err)
+}
+_ = sshConn
+
+

type BannerCallback

+
type BannerCallback func(message string) error
+

BannerCallback is the function type used for treat the banner sent by +the server. A BannerCallback receives the message sent by the remote server. +

func BannerDisplayStderr

+
func BannerDisplayStderr() BannerCallback
+

BannerDisplayStderr returns a function that can be used for +ClientConfig.BannerCallback to display banners on os.Stderr. +

type BannerError

+
type BannerError struct {
+	Err     error
+	Message string
+}
+

BannerError is an error that can be returned by authentication handlers in +Server to send a banner message to the client. +

func (*BannerError) Error

+
func (b *BannerError) Error() string
+

func (*BannerError) Unwrap

+
func (b *BannerError) Unwrap() error
+

type CertChecker

+
type CertChecker struct {
+	// SupportedCriticalOptions lists the CriticalOptions that the
+	// server application layer understands. These are only used
+	// for user certificates.
+	SupportedCriticalOptions []string
+
+	// IsUserAuthority should return true if the key is recognized as an
+	// authority for the given user certificate. This allows for
+	// certificates to be signed by other certificates. This must be set
+	// if this CertChecker will be checking user certificates.
+	IsUserAuthority func(auth PublicKey) bool
+
+	// IsHostAuthority should report whether the key is recognized as
+	// an authority for this host. This allows for certificates to be
+	// signed by other keys, and for those other keys to only be valid
+	// signers for particular hostnames. This must be set if this
+	// CertChecker will be checking host certificates.
+	IsHostAuthority func(auth PublicKey, address string) bool
+
+	// Clock is used for verifying time stamps. If nil, time.Now
+	// is used.
+	Clock func() time.Time
+
+	// UserKeyFallback is called when CertChecker.Authenticate encounters a
+	// public key that is not a certificate. It must implement validation
+	// of user keys or else, if nil, all such keys are rejected.
+	UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+	// HostKeyFallback is called when CertChecker.CheckHostKey encounters a
+	// public key that is not a certificate. It must implement host key
+	// validation or else, if nil, all such keys are rejected.
+	HostKeyFallback HostKeyCallback
+
+	// IsRevoked is called for each certificate so that revocation checking
+	// can be implemented. It should return true if the given certificate
+	// is revoked and false otherwise. If nil, no certificates are
+	// considered to have been revoked.
+	IsRevoked func(cert *Certificate) bool
+}
+

CertChecker does the work of verifying a certificate. Its methods +can be plugged into ClientConfig.HostKeyCallback and +Server.PublicKeyCallback. For the CertChecker to work, +minimally, the IsAuthority callback should be set. +

func (*CertChecker) Authenticate

+
func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error)
+

Authenticate checks a user certificate. Authenticate can be used as +a value for Server.PublicKeyCallback. +

func (*CertChecker) CheckCert

+
func (c *CertChecker) CheckCert(principal string, cert *Certificate) error
+

CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and +the signature of the certificate. +

func (*CertChecker) CheckHostKey

+
func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error
+

CheckHostKey checks a host key certificate. This method can be +plugged into ClientConfig.HostKeyCallback. +

type Certificate

+
type Certificate struct {
+	Nonce           []byte
+	Key             PublicKey
+	Serial          uint64
+	CertType        uint32
+	KeyId           string
+	ValidPrincipals []string
+	ValidAfter      uint64
+	ValidBefore     uint64
+	Permissions
+	Reserved     []byte
+	SignatureKey PublicKey
+	Signature    *Signature
+}
+

An Certificate represents an OpenSSH certificate as defined in +[PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the +PublicKey interface, so it can be unmarshaled using +ParsePublicKey. +

func (*Certificate) Marshal

+
func (c *Certificate) Marshal() []byte
+

Marshal serializes c into OpenSSH's wire format. It is part of the +PublicKey interface. +

func (*Certificate) SignCert

+
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error
+

SignCert signs the certificate with an authority, setting the Nonce, +SignatureKey, and Signature fields. If the authority implements the +MultiAlgorithmSigner interface the first algorithm in the list is used. This +is useful if you want to sign with a specific algorithm. +

+ Example +
package main
+
+import (
+	"crypto/rand"
+	"crypto/rsa"
+	"fmt"
+	"log"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	// Sign a certificate with a specific algorithm.
+	privateKey, err := rsa.GenerateKey(rand.Reader, 3072)
+	if err != nil {
+		log.Fatal("unable to generate RSA key: ", err)
+	}
+	publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
+	if err != nil {
+		log.Fatal("unable to get RSA public key: ", err)
+	}
+	caKey, err := rsa.GenerateKey(rand.Reader, 3072)
+	if err != nil {
+		log.Fatal("unable to generate CA key: ", err)
+	}
+	signer, err := ssh.NewSigner(caKey)
+	if err != nil {
+		log.Fatal("unable to generate signer from key: ", err)
+	}
+	mas, err := ssh.NewSignerWithAlgorithms(signer, []string{ssh.KeyAlgoRSASHA256})
+	if err != nil {
+		log.Fatal("unable to create signer with algorithms: ", err)
+	}
+	certificate := ssh.Certificate{
+		Key:      publicKey,
+		CertType: ssh.UserCert,
+	}
+	if err := certificate.SignCert(rand.Reader, mas); err != nil {
+		log.Fatal("unable to sign certificate: ", err)
+	}
+	// Save the public key to a file and check that rsa-sha-256 is used for
+	// signing:
+	// ssh-keygen -L -f <path to the file>
+	fmt.Println(string(ssh.MarshalAuthorizedKey(&certificate)))
+}
+
+

func (*Certificate) Type

+
func (c *Certificate) Type() string
+

Type returns the certificate algorithm name. It is part of the PublicKey interface. +

func (*Certificate) Verify

+
func (c *Certificate) Verify(data []byte, sig *Signature) error
+

Verify verifies a signature against the certificate's public +key. It is part of the PublicKey interface. +

type Channel

+
type Channel struct {
+	// contains filtered or unexported fields
+}
+

A Channel is an ordered, reliable, flow-controlled, duplex stream +that is multiplexed over an SSH connection. +

func (*Channel) Close

+
func (c *Channel) Close() error
+

Close signals end of channel use. No data may be sent after this call. +

func (*Channel) CloseWrite

+
func (c *Channel) CloseWrite() error
+

CloseWrite signals the end of sending in-band data. Requests may still be +sent, and the other side may still send data. +

func (*Channel) Handle

+
func (c *Channel) Handle(handler RequestHandler) error
+

Handle must be called to handle channel's requests. Handle blocks. If +requestHandler is nil, requests will be discarded. +

func (*Channel) Read

+
func (c *Channel) Read(data []byte) (int, error)
+

Read reads up to len(data) bytes from the channel. +

func (*Channel) SendRequest

+
func (c *Channel) SendRequest(name string, wantReply bool, payload []byte) (bool, error)
+

SendRequest sends a channel request. If wantReply is true, it will wait for a +reply and return the result as a boolean, otherwise the return value will be +false. Channel requests are out-of-band messages so they may be sent even if +the data stream is closed or blocked by flow control. If the channel is +closed before a reply is returned, io.EOF is returned. +

func (*Channel) SetDeadline

+
func (c *Channel) SetDeadline(deadline time.Time) error
+

SetDeadline sets the read and write deadlines associated with the +channel. It is equivalent to calling both SetReadDeadline and +SetWriteDeadline. Deadlines errors are not fatal, the Channel can be used +again after resetting the deadlines. +

func (*Channel) SetReadDeadline

+
func (c *Channel) SetReadDeadline(deadline time.Time) error
+

SetReadDeadline sets the deadline for future Read calls and unblock Read +calls waiting for data. A zero value for t means Read will not time out. +

func (*Channel) SetWriteDeadline

+
func (c *Channel) SetWriteDeadline(deadline time.Time) error
+

SetWriteDeadline sets the deadline for future Write calls and unblock +Write calls waiting for window capacity. A zero value for t means Write +will not time out. +

func (*Channel) Stderr

+
func (c *Channel) Stderr() io.ReadWriter
+

Stderr returns an io.ReadWriter that writes to this channel with the extended +data type set to stderr. Stderr may safely be read and written from a +different goroutine than Read and Write respectively. +

func (*Channel) Write

+
func (c *Channel) Write(data []byte) (int, error)
+

Write writes len(data) bytes to the channel. +

type ChannelHandler

+
type ChannelHandler interface {
+	NewChannel(ch *NewChannel)
+}
+

ChannelHandler defines the interface to handle new channel requests. +

type ChannelHandlerFunc

+
type ChannelHandlerFunc func(ch *NewChannel)
+

ChannelHandlerFunc is an adapter to allow the use of ordinary function as +ChannelHandler. If f is a function with the appropriate signature, +ChannelHandlerFunc(f) is a ChannelHandler that calls f. +

func (ChannelHandlerFunc) NewChannel

+
func (f ChannelHandlerFunc) NewChannel(ch *NewChannel)
+

NewChannel calls f(ch). +

type Client

+
type Client struct {
+	// contains filtered or unexported fields
+}
+

Client implements a traditional SSH client that supports shells, +subprocesses, TCP port/streamlocal forwarding and tunneled dialing. +

func Dial

+
func Dial(ctx context.Context, network, addr string, config *ClientConfig) (*Client, error)
+

Dial starts a client connection to the given SSH server. It is a +convenience function that connects to the given network address, +initiates the SSH handshake, and then sets up a Client. For access +to incoming channels and requests, use net.Dial with NewClientConn +instead. +

+ Example +
package main
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"log"
+	"time"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	var hostKey ssh.PublicKey
+	// An SSH client is represented with a ClientConn.
+	//
+	// To authenticate with the remote server you must pass at least one
+	// implementation of AuthMethod via the Auth field in ClientConfig,
+	// and provide a HostKeyCallback.
+	config := &ssh.ClientConfig{
+		User: "username",
+		Auth: []ssh.AuthMethod{
+			ssh.Password("yourpassword"),
+		},
+		HostKey: ssh.FixedHostKey(hostKey),
+	}
+	// Allow at most 10 seconds to complete the handshake and create the Client.
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+
+	client, err := ssh.Dial(ctx, "tcp", "yourserver.com:22", config)
+	if err != nil {
+		log.Fatal("Failed to dial: ", err)
+	}
+	defer client.Close()
+
+	// Each ClientConn can support multiple interactive sessions,
+	// represented by a Session.
+	session, err := client.NewSession()
+	if err != nil {
+		log.Fatal("Failed to create session: ", err)
+	}
+	defer session.Close()
+
+	// Once a Session is created, you can execute a single command on
+	// the remote side using the Run method.
+	var b bytes.Buffer
+	session.Stdout = &b
+	if err := session.Run("/usr/bin/whoami"); err != nil {
+		log.Fatal("Failed to run: " + err.Error())
+	}
+	fmt.Println(b.String())
+}
+
+

func NewClient

+
func NewClient(c *ClientConn) *Client
+

NewClient creates a Client on top of the given connection. +

func (Client) Close

+
func (c Client) Close() error
+

func (*Client) Dial

+
func (c *Client) Dial(ctx context.Context, n, addr string) (net.Conn, error)
+

Dial initiates a connection to the addr from the remote host. +The resulting connection has a zero LocalAddr() and RemoteAddr(). +

func (*Client) DialTCP

+
func (c *Client) DialTCP(ctx context.Context, n string, laddr, raddr *net.TCPAddr) (net.Conn, error)
+

DialTCP connects to the remote address raddr on the network net, +which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used +as the local address for the connection. +

func (*Client) HandleChannelOpen

+
func (c *Client) HandleChannelOpen(channelType string, handler ChannelHandler) error
+

HandleChannelOpen allows to define a ChannelHandler for the specified +channel type. An error is returned if an handler for the specified type is +already registered. +

func (*Client) Listen

+
func (c *Client) Listen(n, addr string) (net.Listener, error)
+

Listen requests the remote peer open a listening socket on +addr. Incoming connections will be available by calling Accept on +the returned net.Listener. The listener must be serviced, or the +SSH connection may hang. +N must be "tcp", "tcp4", "tcp6", or "unix". +

+ Example +
package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net/http"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	var hostKey ssh.PublicKey
+	config := &ssh.ClientConfig{
+		User: "username",
+		Auth: []ssh.AuthMethod{
+			ssh.Password("password"),
+		},
+		HostKey: ssh.FixedHostKey(hostKey),
+	}
+	// Dial your ssh server.
+	conn, err := ssh.Dial(context.Background(), "tcp", "localhost:22", config)
+	if err != nil {
+		log.Fatal("unable to connect: ", err)
+	}
+	defer conn.Close()
+
+	// Request the remote side to open port 8080 on all interfaces.
+	l, err := conn.Listen("tcp", "0.0.0.0:8080")
+	if err != nil {
+		log.Fatal("unable to register tcp forward: ", err)
+	}
+	defer l.Close()
+
+	// Serve HTTP with your SSH server acting as a reverse proxy.
+	http.Serve(l, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+		fmt.Fprintf(resp, "Hello world!\n")
+	}))
+}
+
+

func (*Client) ListenUnix

+
func (c *Client) ListenUnix(socketPath string) (net.Listener, error)
+

ListenUnix is similar to ListenTCP but uses a Unix domain socket. +

func (*Client) NewSession

+
func (c *Client) NewSession() (*Session, error)
+

NewSession opens a new Session for this client. (A session is a remote +execution of a program.) +

type ClientConfig

+
type ClientConfig struct {
+	// Config contains configuration that is shared between clients and
+	// servers.
+	Config
+
+	// User contains the username to authenticate as.
+	User string
+
+	// Auth contains possible authentication methods to use with the
+	// server. Only the first instance of a particular RFC 4252 method will
+	// be used during authentication.
+	Auth []AuthMethod
+
+	// HostKey is called during the cryptographic
+	// handshake to validate the server's host key. The client
+	// configuration must supply this callback for the connection
+	// to succeed. The functions InsecureIgnoreHostKey or
+	// FixedHostKey can be used for simplistic host key checks.
+	HostKey HostKeyCallback
+
+	// Banner is called during the SSH dance to display a custom
+	// server's message. The client configuration can supply this callback to
+	// handle it as wished. The function BannerDisplayStderr can be used for
+	// simplistic display on Stderr.
+	Banner BannerCallback
+
+	// ClientVersion contains the version identification string that will
+	// be used for the connection. If empty, a reasonable default is used.
+	ClientVersion string
+
+	// HostKeyAlgorithms lists the public key algorithms that the client will
+	// accept from the server for host key authentication, in order of
+	// preference. If empty, a reasonable default is used. Any
+	// string returned from a PublicKey.Type method may be used, or
+	// any of the CertAlgo and KeyAlgo constants.
+	HostKeyAlgorithms []string
+}
+

A ClientConfig structure is used to configure a Client. It must not be +modified after having been passed to an SSH function. +

type ClientConn

+
type ClientConn struct {
+	// contains filtered or unexported fields
+}
+

ClientConn is an authenticated SSH connection, as seen from the +client +

func NewClientConn

+
func NewClientConn(c net.Conn, addr string, config *ClientConfig) (*ClientConn, error)
+

NewClientConn establishes an authenticated SSH connection using c as the +underlying transport. You can use NewClient to build an SSH client or +handle this client connection yourself by using ClientConn.Handle. +

func (ClientConn) Close

+
func (c ClientConn) Close() error
+

func (*ClientConn) Handle

+
func (c *ClientConn) Handle(channelHandler ChannelHandler, requestHandler RequestHandler) error
+

Handle must be called to handle requests and channels if you want to handle a +ClientConn yourself without building a Client using NewClient. Handle +blocks. If channelHandler is nil channels will be rejected. If requestHandler +is nil, requests will be discarded. +

type ClientHandler

+
type ClientHandler interface {
+	// HandleClient is called after the handshake completes and a client
+	// authenticates with the server.
+	HandleClient(conn *ServerConn)
+}
+

ClientHandler defines the interface to handle authenticated server +connections. +

type ClientHandlerFunc

+
type ClientHandlerFunc func(conn *ServerConn)
+

ClientHandlerFunc is an adapter to allow the use of ordinary function as +ClientHandler. If f is a function with the appropriate signature, +ClientHandlerFunc(f) is a ClientHandler that calls f. +

func (ClientHandlerFunc) HandleClient

+
func (f ClientHandlerFunc) HandleClient(conn *ServerConn)
+

HandleClient calls f(conn). +

type Config

+
type Config struct {
+	// Rand provides the source of entropy for cryptographic
+	// primitives. If Rand is nil, the cryptographic random reader
+	// in package crypto/rand will be used.
+	Rand io.Reader
+
+	// The maximum number of bytes sent or received after which a
+	// new key is negotiated. It must be at least 256. If
+	// unspecified, a size suitable for the chosen cipher is used.
+	RekeyThreshold uint64
+
+	// The allowed key exchanges algorithms. If unspecified then a default set
+	// of algorithms is used. Unsupported values are silently ignored.
+	KeyExchanges []string
+
+	// The allowed cipher algorithms. If unspecified then a sensible default is
+	// used. Unsupported values are silently ignored.
+	Ciphers []string
+
+	// The allowed MAC algorithms. If unspecified then a sensible default is
+	// used. Unsupported values are silently ignored.
+	MACs []string
+}
+

Config contains configuration data common to both Server and +ClientConfig. +

func (*Config) SetDefaults

+
func (c *Config) SetDefaults()
+

SetDefaults sets sensible values for unset fields in config. This is +exported for testing: Configs passed to SSH functions are copied and have +default values set automatically. +

type ConnMetadata

+
type ConnMetadata struct {
+	// contains filtered or unexported fields
+}
+

ConnMetadata holds metadata for the connection. +

func (ConnMetadata) ClientVersion

+
func (c ConnMetadata) ClientVersion() []byte
+

ClientVersion returns the client's version string as hashed into the session +ID. +

func (ConnMetadata) LocalAddr

+
func (c ConnMetadata) LocalAddr() net.Addr
+

LocalAddr returns the local address for this connection. +

func (ConnMetadata) RemoteAddr

+
func (c ConnMetadata) RemoteAddr() net.Addr
+

RemoteAddr returns the remote address for this connection. +

func (ConnMetadata) ServerVersion

+
func (c ConnMetadata) ServerVersion() []byte
+

ServerVersion returns the server's version string as hashed into the session +ID. +

func (ConnMetadata) SessionID

+
func (c ConnMetadata) SessionID() []byte
+

SessionID returns the session hash, also denoted by H. +

func (ConnMetadata) User

+
func (c ConnMetadata) User() string
+

User returns the user ID for this connection. +

type CryptoPublicKey

+
type CryptoPublicKey interface {
+	CryptoPublicKey() crypto.PublicKey
+}
+

CryptoPublicKey, if implemented by a PublicKey, +returns the underlying crypto.PublicKey form of the key. +

type ExitError

+
type ExitError struct {
+	Waitmsg
+}
+

An ExitError reports unsuccessful completion of a remote command. +

func (*ExitError) Error

+
func (e *ExitError) Error() string
+

type ExitMissingError

+
type ExitMissingError struct{}
+

ExitMissingError is returned if a session is torn down cleanly, but +the server sends no confirmation of the exit status. +

func (*ExitMissingError) Error

+
func (e *ExitMissingError) Error() string
+

type GSSAPIClient

+
type GSSAPIClient interface {
+	// InitSecContext initiates the establishment of a security context for GSS-API between the
+	// ssh client and ssh server. Initially the token parameter should be specified as nil.
+	// The routine may return a outputToken which should be transferred to
+	// the ssh server, where the ssh server will present it to
+	// AcceptSecContext. If no token need be sent, InitSecContext will indicate this by setting
+	// needContinue to false. To complete the context
+	// establishment, one or more reply tokens may be required from the ssh
+	// server;if so, InitSecContext will return a needContinue which is true.
+	// In this case, InitSecContext should be called again when the
+	// reply token is received from the ssh server, passing the reply
+	// token to InitSecContext via the token parameters.
+	// See RFC 2743 section 2.2.1 and RFC 4462 section 3.4.
+	InitSecContext(target string, token []byte, isGSSDelegCreds bool) (outputToken []byte, needContinue bool, err error)
+	// GetMIC generates a cryptographic MIC for the SSH2 message, and places
+	// the MIC in a token for transfer to the ssh server.
+	// The contents of the MIC field are obtained by calling GSS_GetMIC()
+	// over the following, using the GSS-API context that was just
+	// established:
+	//  string    session identifier
+	//  byte      SSH_MSG_USERAUTH_REQUEST
+	//  string    user name
+	//  string    service
+	//  string    "gssapi-with-mic"
+	// See RFC 2743 section 2.3.1 and RFC 4462 3.5.
+	GetMIC(micFiled []byte) ([]byte, error)
+	// Whenever possible, it should be possible for
+	// DeleteSecContext() calls to be successfully processed even
+	// if other calls cannot succeed, thereby enabling context-related
+	// resources to be released.
+	// In addition to deleting established security contexts,
+	// gss_delete_sec_context must also be able to delete "half-built"
+	// security contexts resulting from an incomplete sequence of
+	// InitSecContext()/AcceptSecContext() calls.
+	// See RFC 2743 section 2.2.3.
+	DeleteSecContext() error
+}
+

GSSAPIClient provides the API to plug-in GSSAPI authentication for client logins. +

type GSSAPIServer

+
type GSSAPIServer interface {
+	// AcceptSecContext allows a remotely initiated security context between the application
+	// and a remote peer to be established by the ssh client. The routine may return a
+	// outputToken which should be transferred to the ssh client,
+	// where the ssh client will present it to InitSecContext.
+	// If no token need be sent, AcceptSecContext will indicate this
+	// by setting the needContinue to false. To
+	// complete the context establishment, one or more reply tokens may be
+	// required from the ssh client. if so, AcceptSecContext
+	// will return a needContinue which is true, in which case it
+	// should be called again when the reply token is received from the ssh
+	// client, passing the token to AcceptSecContext via the
+	// token parameters.
+	// The srcName return value is the authenticated username.
+	// See RFC 2743 section 2.2.2 and RFC 4462 section 3.4.
+	AcceptSecContext(token []byte) (outputToken []byte, srcName string, needContinue bool, err error)
+	// VerifyMIC verifies that a cryptographic MIC, contained in the token parameter,
+	// fits the supplied message is received from the ssh client.
+	// See RFC 2743 section 2.3.2.
+	VerifyMIC(micField []byte, micToken []byte) error
+	// Whenever possible, it should be possible for
+	// DeleteSecContext() calls to be successfully processed even
+	// if other calls cannot succeed, thereby enabling context-related
+	// resources to be released.
+	// In addition to deleting established security contexts,
+	// gss_delete_sec_context must also be able to delete "half-built"
+	// security contexts resulting from an incomplete sequence of
+	// InitSecContext()/AcceptSecContext() calls.
+	// See RFC 2743 section 2.2.3.
+	DeleteSecContext() error
+}
+

GSSAPIServer provides the API to plug in GSSAPI authentication for server logins. +

type GSSAPIWithMICConfig

+
type GSSAPIWithMICConfig struct {
+	// AllowLogin, must be set, is called when gssapi-with-mic
+	// authentication is selected (RFC 4462 section 3). The srcName is from the
+	// results of the GSS-API authentication. The format is username@DOMAIN.
+	// GSSAPI just guarantees to the server who the user is, but not if they can log in, and with what permissions.
+	// This callback is called after the user identity is established with GSSAPI to decide if the user can login with
+	// which permissions. If the user is allowed to login, it should return a nil error.
+	AllowLogin func(conn ConnMetadata, srcName string) (*Permissions, error)
+
+	// Server must be set. It's the implementation
+	// of the GSSAPIServer interface. See GSSAPIServer interface for details.
+	Server GSSAPIServer
+}
+

type HostKeyCallback

+
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
+

HostKeyCallback is the function type used for verifying server +keys. A HostKeyCallback must return nil if the host key is OK, or +an error to reject it. It receives the hostname as passed to Dial +or NewClientConn. The remote address is the RemoteAddr of the +net.Conn underlying the SSH connection. +

func FixedHostKey

+
func FixedHostKey(key PublicKey) HostKeyCallback
+

FixedHostKey returns a function for use in +ClientConfig.HostKeyCallback to accept only a specific host key. +

func InsecureIgnoreHostKey

+
func InsecureIgnoreHostKey() HostKeyCallback
+

InsecureIgnoreHostKey returns a function that can be used for +ClientConfig.HostKeyCallback to accept any host key. It should +not be used for production code. +

type KeyboardInteractiveChallenge

+
type KeyboardInteractiveChallenge func(name, instruction string, questions []string, echos []bool) (answers []string, err error)
+

KeyboardInteractiveChallenge should print questions, optionally +disabling echoing (e.g. for passwords), and return all the answers. +Challenge may be called multiple times in a single session. After +successful authentication, the server may send a challenge with no +questions, for which the name and instruction messages should be +printed. RFC 4256 section 3.3 details how the UI should behave for +both CLI and GUI environments. +

type NewChannel

+
type NewChannel struct {
+	// contains filtered or unexported fields
+}
+

NewChannel represents an incoming request to a channel. It must either be +accepted for use by calling Accept, or rejected by calling Reject. +

func (*NewChannel) Accept

+
func (c *NewChannel) Accept() (*Channel, error)
+

Accept accepts the channel creation request. The returned channel must be +serviced using Channel.Handle. +

func (*NewChannel) ChannelType

+
func (c *NewChannel) ChannelType() string
+

ChannelType returns the type of the channel, as supplied by the +client. +

func (*NewChannel) ExtraData

+
func (c *NewChannel) ExtraData() []byte
+

ExtraData returns the arbitrary payload for this channel, as supplied +by the client. This data is specific to the channel type. +

func (*NewChannel) Reject

+
func (c *NewChannel) Reject(reason RejectionReason, message string) error
+

Reject rejects the channel creation request. After calling +this, no other methods on the Channel may be called. +

type OpenChannelError

+
type OpenChannelError struct {
+	Reason  RejectionReason
+	Message string
+}
+

OpenChannelError is returned if the other side rejects an +OpenChannel request. +

func (*OpenChannelError) Error

+
func (e *OpenChannelError) Error() string
+

type PartialSuccessError

+
type PartialSuccessError struct {
+	// Next defines the authentication callbacks to apply to further steps. The
+	// available methods communicated to the client are based on the non-nil
+	// ServerAuthCallbacks fields.
+	Next ServerAuthCallbacks
+}
+

PartialSuccessError can be returned by any of the Server +authentication callbacks to indicate to the client that authentication has +partially succeeded, but further steps are required. +

func (*PartialSuccessError) Error

+
func (p *PartialSuccessError) Error() string
+

type PassphraseMissingError

+
type PassphraseMissingError struct {
+	// PublicKey will be set if the private key format includes an unencrypted
+	// public key along with the encrypted private key.
+	PublicKey PublicKey
+}
+

A PassphraseMissingError indicates that parsing this private key requires a +passphrase. Use ParsePrivateKeyWithPassphrase. +

func (*PassphraseMissingError) Error

+
func (*PassphraseMissingError) Error() string
+

type Permissions

+
type Permissions struct {
+	// CriticalOptions indicate restrictions to the default
+	// permissions, and are typically used in conjunction with
+	// user certificates. The standard for SSH certificates
+	// defines "force-command" (only allow the given command to
+	// execute) and "source-address" (only allow connections from
+	// the given address). The SSH package currently only enforces
+	// the "source-address" critical option. It is up to server
+	// implementations to enforce other critical options, such as
+	// "force-command", by checking them after the SSH handshake
+	// is successful. In general, SSH servers should reject
+	// connections that specify critical options that are unknown
+	// or not supported.
+	CriticalOptions map[string]string
+
+	// Extensions are extra functionality that the server may
+	// offer on authenticated connections. Lack of support for an
+	// extension does not preclude authenticating a user. Common
+	// extensions are "permit-agent-forwarding",
+	// "permit-X11-forwarding". The Go SSH library currently does
+	// not act on any extension, and it is up to server
+	// implementations to honor them. Extensions can be used to
+	// pass data from the authentication callbacks to the server
+	// application layer.
+	Extensions map[string]string
+}
+

The Permissions type holds fine-grained permissions that are +specific to a user or a specific authentication method for a user. +The Permissions value for a successful authentication attempt is +available in ServerConn, so it can be used to pass information from +the user-authentication phase to the application layer. +

type PublicKey

+
type PublicKey interface {
+	// Type returns the key format name, e.g. "ssh-rsa".
+	Type() string
+
+	// Marshal returns the serialized key data in SSH wire format, with the name
+	// prefix. To unmarshal the returned data, use the ParsePublicKey function.
+	Marshal() []byte
+
+	// Verify that sig is a signature on the given data using this key. This
+	// method will hash the data appropriately first. sig.Format is allowed to
+	// be any signature algorithm compatible with the key type, the caller
+	// should check if it has more stringent requirements.
+	Verify(data []byte, sig *Signature) error
+}
+

PublicKey represents a public key using an unspecified algorithm. +

Some PublicKeys provided by this package also implement CryptoPublicKey. +

func NewPublicKey

+
func NewPublicKey(key interface{}) (PublicKey, error)
+

NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, +or ed25519.PublicKey returns a corresponding PublicKey instance. +ECDSA keys must use P-256, P-384 or P-521. +

func ParseAuthorizedKey

+
func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error)
+

ParseAuthorizedKey parses a public key from an authorized_keys +file used in OpenSSH according to the sshd(8) manual page. +

func ParseKnownHosts

+
func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error)
+

ParseKnownHosts parses an entry in the format of the known_hosts file. +

The known_hosts format is documented in the sshd(8) manual page. This +function will parse a single entry from in. On successful return, marker +will contain the optional marker value (i.e. "cert-authority" or "revoked") +or else be empty, hosts will contain the hosts that this entry matches, +pubKey will contain the public key and comment will contain any trailing +comment at the end of the line. See the sshd(8) manual page for the various +forms that a host string can take. +

The unparsed remainder of the input will be returned in rest. This function +can be called repeatedly to parse multiple entries. +

If no entries were found in the input then err will be io.EOF. Otherwise a +non-nil err value indicates a parse error. +

func ParsePublicKey

+
func ParsePublicKey(in []byte) (out PublicKey, err error)
+

ParsePublicKey parses an SSH public key formatted for use in +the SSH wire protocol according to RFC 4253, section 6.6. +

type RejectionReason

+
type RejectionReason uint32
+

RejectionReason is an enumeration used when rejecting channel creation +requests. See RFC 4254, section 5.1. +

const (
+	Prohibited RejectionReason = iota + 1
+	ConnectionFailed
+	UnknownChannelType
+	ResourceShortage
+)
+

func (RejectionReason) String

+
func (r RejectionReason) String() string
+

String converts the rejection reason to human readable form. +

type Request

+
type Request struct {
+	Type      string
+	WantReply bool
+	Payload   []byte
+	// contains filtered or unexported fields
+}
+

Request is a request sent outside of the normal stream of +data. Requests can either be specific to an SSH channel, or they +can be global. +

func (*Request) Reply

+
func (r *Request) Reply(ok bool, payload []byte) error
+

Reply sends a response to a request. It must be called for all requests +where WantReply is true and is a no-op otherwise. The payload argument is +ignored for replies to channel-specific requests. +

type RequestHandler

+
type RequestHandler interface {
+	NewRequest(req *Request)
+}
+

RequestHandler defines the interface to handle new Request.ListenTCP +

type RequestHandlerFunc

+
type RequestHandlerFunc func(req *Request)
+

RequestHandlerFunc is an adapter to allow the use of ordinary function as +RequestHandler. If f is a function with the appropriate signature, +RequestHandlerFunc(f) is a RequestHandler that calls f. +

func (RequestHandlerFunc) NewRequest

+
func (f RequestHandlerFunc) NewRequest(req *Request)
+

NewRequest calls f(req). +

type Server

+
type Server struct {
+	// Config contains configuration shared between client and server.
+	Config
+
+	// PublicKeyAuthAlgorithms specifies the supported client public key
+	// authentication algorithms. Note that this should not include certificate
+	// types since those use the underlying algorithm. This list is sent to the
+	// client if it supports the server-sig-algs extension. Order is irrelevant.
+	// If unspecified then a default set of algorithms is used.
+	PublicKeyAuthAlgorithms []string
+
+	// NoClientAuth, if non-nil, is called when a user
+	// attempts to authenticate with auth method "none".
+	NoClientAuth func(ConnMetadata) (*Permissions, error)
+
+	// MaxAuthTries specifies the maximum number of authentication attempts
+	// permitted per connection. If set to a negative number, the number of
+	// attempts are unlimited. If set to zero, the number of attempts are limited
+	// to 6.
+	MaxAuthTries int
+
+	// Password, if non-nil, is called when a user
+	// attempts to authenticate using a password.
+	Password func(conn ConnMetadata, password []byte) (*Permissions, error)
+
+	// PublicKey, if non-nil, is called when a client
+	// offers a public key for authentication. It must return a nil error
+	// if the given public key can be used to authenticate the
+	// given user. For example, see CertChecker.Authenticate. A
+	// call to this function does not guarantee that the key
+	// offered is in fact used to authenticate. To record any data
+	// depending on the public key, store it inside a
+	// Permissions.Extensions entry.
+	PublicKey func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+	// KeyboardInteractive, if non-nil, is called when
+	// keyboard-interactive authentication is selected (RFC
+	// 4256). The client object's Challenge function should be
+	// used to query the user. The callback may offer multiple
+	// Challenge rounds. To avoid information leaks, the client
+	// should be presented a challenge even if the user is
+	// unknown.
+	KeyboardInteractive func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
+
+	// AuthLog, if non-nil, is called to log all authentication
+	// attempts.
+	AuthLog func(conn ConnMetadata, method string, err error)
+
+	// ServerVersion is the version identification string to announce in
+	// the public handshake.
+	// If empty, a reasonable default is used.
+	// Note that RFC 4253 section 4.2 requires that this string start with
+	// "SSH-2.0-".
+	ServerVersion string
+
+	// BannerCallback, if present, is called and the return string is sent to
+	// the client after key exchange completed but before authentication.
+	Banner func(conn ConnMetadata) string
+
+	// GSSAPIWithMICConfig includes gssapi server and callback, which if both non-nil, is used
+	// when gssapi-with-mic authentication is selected (RFC 4462 section 3).
+	GSSAPIWithMICConfig *GSSAPIWithMICConfig
+
+	// HandshakeTimeout defines the timeout for the initial handshake, as milliseconds.
+	HandshakeTimeout int
+
+	// ConnectionFailed, if non-nil, is called to report handshake errors.
+	ConnectionFailed func(c net.Conn, err error)
+
+	// ConnectionAddedCallback, if non-nil, is called when a client connects, by
+	// returning an error the connection will be refused.
+	ConnectionAdded func(c net.Conn) error
+
+	// ClientHandler defines the handler for authenticated clients. It is called
+	// if the handshake is successfull. The handler must serve requests and
+	// channels using [ServerConn.Handle].
+	ClientHandler ClientHandler
+	// contains filtered or unexported fields
+}
+

Server holds server specific configuration data. +

func (*Server) AddHostKey

+
func (s *Server) AddHostKey(key Signer)
+

AddHostKey adds a private key as a host key. If an existing host +key exists with the same public key format, it is replaced. Each server +config must have at least one host key. +

+ Example +
package main
+
+import (
+	"fmt"
+	"log"
+	"os"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	// Minimal Server supporting only password authentication.
+	config := &ssh.Server{
+		Password: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
+			// Should use constant-time compare (or better, salt+hash) in
+			// a production setting.
+			if c.User() == "testuser" && string(pass) == "tiger" {
+				return nil, nil
+			}
+			return nil, fmt.Errorf("password rejected for %q", c.User())
+		},
+	}
+
+	privateBytes, err := os.ReadFile("id_rsa")
+	if err != nil {
+		log.Fatal("Failed to load private key: ", err)
+	}
+
+	private, err := ssh.ParsePrivateKey(privateBytes)
+	if err != nil {
+		log.Fatal("Failed to parse private key: ", err)
+	}
+	// Restrict host key algorithms to disable ssh-rsa.
+	signer, err := ssh.NewSignerWithAlgorithms(private, []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
+	if err != nil {
+		log.Fatal("Failed to create private key with restricted algorithms: ", err)
+	}
+	config.AddHostKey(signer)
+}
+
+

func (*Server) Close

+
func (s *Server) Close() error
+

Close immediately closes all active net.Listeners. +

func (*Server) ListenAndServe

+
func (s *Server) ListenAndServe(addr string) error
+
+ Example +
package main
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"os"
+
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/term"
+)
+
+func main() {
+	// Public key authentication is done by comparing
+	// the public key of a received connection
+	// with the entries in the authorized_keys file.
+	authorizedKeysBytes, err := os.ReadFile("authorized_keys")
+	if err != nil {
+		log.Fatalf("Failed to load authorized_keys, err: %v", err)
+	}
+
+	authorizedKeysMap := map[string]bool{}
+	for len(authorizedKeysBytes) > 0 {
+		pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		authorizedKeysMap[string(pubKey.Marshal())] = true
+		authorizedKeysBytes = rest
+	}
+
+	// An SSH server is represented by a Server, which holds
+	// certificate details and handles authentication of ServerConns.
+	server := &ssh.Server{
+		// Remove to disable password auth.
+		Password: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
+			// Should use constant-time compare (or better, salt+hash) in
+			// a production setting.
+			if c.User() == "testuser" && string(pass) == "tiger" {
+				return nil, nil
+			}
+			return nil, fmt.Errorf("password rejected for %q", c.User())
+		},
+
+		// Remove to disable public key auth.
+		PublicKey: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
+			if authorizedKeysMap[string(pubKey.Marshal())] {
+				return &ssh.Permissions{
+					// Record the public key used for authentication.
+					Extensions: map[string]string{
+						"pubkey-fp": ssh.FingerprintSHA256(pubKey),
+					},
+				}, nil
+			}
+			return nil, fmt.Errorf("unknown public key for %q", c.User())
+		},
+		ConnectionFailed: func(c net.Conn, err error) {
+			// Here we can get the error if an handshake fails
+		},
+		ClientHandler: ssh.ClientHandlerFunc(func(conn *ssh.ServerConn) {
+			conn.Handle(
+				ssh.ChannelHandlerFunc(func(newChannel *ssh.NewChannel) {
+					// In this handler we have to accept and use the new channel or reject it.
+
+					// Channels have a type, depending on the application level
+					// protocol intended. In the case of a shell, the type is
+					// "session" and ServerShell may be used to present a simple
+					// terminal interface.
+					if newChannel.ChannelType() != "session" {
+						newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
+						return
+					}
+					channel, err := newChannel.Accept()
+					if err != nil {
+						log.Fatalf("Could not accept channel: %v", err)
+					}
+					err = channel.Handle(ssh.RequestHandlerFunc(func(req *ssh.Request) {
+						// Sessions have out-of-band requests such as "shell",
+						// "pty-req" and "env".  Here we handle only the
+						// "shell" request.
+						req.Reply(req.Type == "shell", nil)
+					}))
+
+					terminal := term.NewTerminal(channel, "> ")
+
+					go func() {
+						defer func() {
+							channel.Close()
+						}()
+						for {
+							line, err := terminal.ReadLine()
+							if err != nil {
+								break
+							}
+							fmt.Println(line)
+						}
+					}()
+				}),
+				ssh.RequestHandlerFunc(func(req *ssh.Request) {
+					if req.WantReply {
+						req.Reply(false, nil)
+					}
+				}),
+			)
+		}),
+	}
+
+	privateBytes, err := os.ReadFile("id_rsa")
+	if err != nil {
+		log.Fatal("Failed to load private key: ", err)
+	}
+
+	private, err := ssh.ParsePrivateKey(privateBytes)
+	if err != nil {
+		log.Fatal("Failed to parse private key: ", err)
+	}
+	server.AddHostKey(private)
+
+	server.ListenAndServe("0.0.0.0:2022")
+}
+
+

func (*Server) Serve

+
func (s *Server) Serve(l net.Listener) error
+

Serve accepts incoming connections on the Listener l, creating a new service +goroutine for each and execute the provided ClientHandler implementation. +

type ServerAuthCallbacks

+
type ServerAuthCallbacks struct {
+	// Password behaves like [Server.PasswordCallback].
+	Password func(conn ConnMetadata, password []byte) (*Permissions, error)
+
+	// PublicKey behaves like [Server.PublicKeyCallback].
+	PublicKey func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+	// KeyboardInteractive behaves like [Server.KeyboardInteractiveCallback].
+	KeyboardInteractive func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error)
+
+	// GSSAPIWithMICConfig behaves like [Server.GSSAPIWithMICConfig].
+	GSSAPIWithMICConfig *GSSAPIWithMICConfig
+}
+

ServerAuthCallbacks defines server-side authentication callbacks. +

type ServerAuthError

+
type ServerAuthError struct {
+	// Errors contains authentication errors returned by the authentication
+	// callback methods. The first entry is typically ErrNoAuth.
+	Errors []error
+}
+

ServerAuthError represents server authentication errors and is +sometimes returned by NewServerConn. It appends any authentication +errors that may occur, and is returned if all of the authentication +methods provided by the user failed to authenticate. +

func (ServerAuthError) Error

+
func (l ServerAuthError) Error() string
+

type ServerConn

+
type ServerConn struct {
+
+	// If the succeeding authentication callback returned a
+	// non-nil Permissions pointer, it is stored here.
+	Permissions *Permissions
+	// contains filtered or unexported fields
+}
+

ServerConn is an authenticated SSH connection, as seen from the +server +

func NewServerConn

+
func NewServerConn(ctx context.Context, c net.Conn, config *Server) (*ServerConn, error)
+

NewServerConn starts a new SSH server with c as the underlying +transport. It starts with a handshake and, if the handshake is +unsuccessful, it closes the connection and returns an error. The +Request and NewChannel channels must be serviced, or the connection +will hang. +

The returned error may be of type *ServerAuthError for +authentication errors. +

This is a low level API useful for advanced use cases, you must use +ServerConn.Handle to handle Requests and Channels. +

+ Example +
package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"sync"
+	"time"
+
+	"golang.org/x/crypto/ssh"
+	"golang.org/x/term"
+)
+
+func main() {
+	// Public key authentication is done by comparing
+	// the public key of a received connection
+	// with the entries in the authorized_keys file.
+	authorizedKeysBytes, err := os.ReadFile("authorized_keys")
+	if err != nil {
+		log.Fatalf("Failed to load authorized_keys, err: %v", err)
+	}
+
+	authorizedKeysMap := map[string]bool{}
+	for len(authorizedKeysBytes) > 0 {
+		pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
+		if err != nil {
+			log.Fatal(err)
+		}
+
+		authorizedKeysMap[string(pubKey.Marshal())] = true
+		authorizedKeysBytes = rest
+	}
+
+	// An SSH server is represented by a Server, which holds
+	// certificate details and handles authentication of ServerConns.
+	config := &ssh.Server{
+		// Remove to disable password auth.
+		Password: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
+			// Should use constant-time compare (or better, salt+hash) in
+			// a production setting.
+			if c.User() == "testuser" && string(pass) == "tiger" {
+				return nil, nil
+			}
+			return nil, fmt.Errorf("password rejected for %q", c.User())
+		},
+
+		// Remove to disable public key auth.
+		PublicKey: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
+			if authorizedKeysMap[string(pubKey.Marshal())] {
+				return &ssh.Permissions{
+					// Record the public key used for authentication.
+					Extensions: map[string]string{
+						"pubkey-fp": ssh.FingerprintSHA256(pubKey),
+					},
+				}, nil
+			}
+			return nil, fmt.Errorf("unknown public key for %q", c.User())
+		},
+	}
+
+	privateBytes, err := os.ReadFile("id_rsa")
+	if err != nil {
+		log.Fatal("Failed to load private key: ", err)
+	}
+
+	private, err := ssh.ParsePrivateKey(privateBytes)
+	if err != nil {
+		log.Fatal("Failed to parse private key: ", err)
+	}
+	config.AddHostKey(private)
+
+	// Once a Server has been configured, connections can be
+	// accepted.
+	listener, err := net.Listen("tcp", "0.0.0.0:2022")
+	if err != nil {
+		log.Fatal("failed to listen for connection: ", err)
+	}
+	nConn, err := listener.Accept()
+	if err != nil {
+		log.Fatal("failed to accept incoming connection: ", err)
+	}
+
+	// Allow at most 10 seconds to complete the handshake and create the
+	// server connection.
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+
+	// Before use, a handshake must be performed on the incoming
+	// net.Conn.
+	conn, err := ssh.NewServerConn(ctx, nConn, config)
+	if err != nil {
+		log.Fatal("failed to handshake: ", err)
+	}
+	log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
+
+	var wg sync.WaitGroup
+	defer wg.Wait()
+
+	conn.Handle(
+		ssh.ChannelHandlerFunc(func(newChannel *ssh.NewChannel) {
+			// Channels have a type, depending on the application level
+			// protocol intended. In the case of a shell, the type is
+			// "session" and ServerShell may be used to present a simple
+			// terminal interface.
+			if newChannel.ChannelType() != "session" {
+				newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
+				return
+			}
+			channel, err := newChannel.Accept()
+			if err != nil {
+				log.Fatalf("Could not accept channel: %v", err)
+			}
+			err = channel.Handle(ssh.RequestHandlerFunc(func(req *ssh.Request) {
+				// Sessions have out-of-band requests such as "shell",
+				// "pty-req" and "env".  Here we handle only the
+				// "shell" request.
+				req.Reply(req.Type == "shell", nil)
+			}))
+			if err != nil {
+				log.Fatalf("Could not handle channel: %v", err)
+			}
+
+			terminal := term.NewTerminal(channel, "> ")
+
+			wg.Add(1)
+			go func() {
+				defer func() {
+					channel.Close()
+					wg.Done()
+				}()
+				for {
+					line, err := terminal.ReadLine()
+					if err != nil {
+						break
+					}
+					fmt.Println(line)
+				}
+			}()
+		}),
+		ssh.RequestHandlerFunc(func(req *ssh.Request) {
+			if req.WantReply {
+				req.Reply(false, nil)
+			}
+		}))
+}
+
+

func (*ServerConn) Close

+
func (c *ServerConn) Close() error
+

Close closes the underlying network connection. +

func (*ServerConn) Handle

+
func (c *ServerConn) Handle(channelHandler ChannelHandler, requestHandler RequestHandler) error
+

Handle must be called to handle requests and channels. Handle blocks. If +channelHandler is nil channels will be rejected. If requestHandler is nil, +requests will be discarded. +

type Session

+
type Session struct {
+	// Stdin specifies the remote process's standard input.
+	// If Stdin is nil, the remote process reads from an empty
+	// bytes.Buffer.
+	Stdin io.Reader
+
+	// Stdout and Stderr specify the remote process's standard
+	// output and error.
+	//
+	// If either is nil, Run connects the corresponding file
+	// descriptor to an instance of io.Discard. There is a
+	// fixed amount of buffering that is shared for the two streams.
+	// If either blocks it may eventually cause the remote
+	// command to block.
+	Stdout io.Writer
+	Stderr io.Writer
+	// contains filtered or unexported fields
+}
+

A Session represents a connection to a remote command or shell. +

func (*Session) Close

+
func (s *Session) Close() error
+

func (*Session) CombinedOutput

+
func (s *Session) CombinedOutput(cmd string) ([]byte, error)
+

CombinedOutput runs cmd on the remote host and returns its combined +standard output and standard error. +

func (*Session) Output

+
func (s *Session) Output(cmd string) ([]byte, error)
+

Output runs cmd on the remote host and returns its standard output. +

func (*Session) RequestPty

+
func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error
+

RequestPty requests the association of a pty with the session on the remote host. +

+ Example +
package main
+
+import (
+	"context"
+	"log"
+
+	"golang.org/x/crypto/ssh"
+)
+
+func main() {
+	var hostKey ssh.PublicKey
+	// Create client config
+	config := &ssh.ClientConfig{
+		User: "username",
+		Auth: []ssh.AuthMethod{
+			ssh.Password("password"),
+		},
+		HostKey: ssh.FixedHostKey(hostKey),
+	}
+	// Connect to ssh server
+	conn, err := ssh.Dial(context.Background(), "tcp", "localhost:22", config)
+	if err != nil {
+		log.Fatal("unable to connect: ", err)
+	}
+	defer conn.Close()
+	// Create a session
+	session, err := conn.NewSession()
+	if err != nil {
+		log.Fatal("unable to create session: ", err)
+	}
+	defer session.Close()
+	// Set up terminal modes
+	modes := ssh.TerminalModes{
+		ssh.ECHO:          0,     // disable echoing
+		ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
+		ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
+	}
+	// Request pseudo terminal
+	if err := session.RequestPty("xterm", 40, 80, modes); err != nil {
+		log.Fatal("request for pseudo terminal failed: ", err)
+	}
+	// Start remote shell
+	if err := session.Shell(); err != nil {
+		log.Fatal("failed to start shell: ", err)
+	}
+}
+
+

func (*Session) RequestSubsystem

+
func (s *Session) RequestSubsystem(subsystem string) error
+

RequestSubsystem requests the association of a subsystem with the session on the remote host. +A subsystem is a predefined command that runs in the background when the ssh session is initiated +

func (*Session) Run

+
func (s *Session) Run(cmd string) error
+

Run runs cmd on the remote host. Typically, the remote +server passes cmd to the shell for interpretation. +A Session only accepts one call to Run, Start, Shell, Output, +or CombinedOutput. +

The returned error is nil if the command runs, has no problems +copying stdin, stdout, and stderr, and exits with a zero exit +status. +

If the remote server does not send an exit status, an error of type +*ExitMissingError is returned. If the command completes +unsuccessfully or is interrupted by a signal, the error is of type +*ExitError. Other error types may be returned for I/O problems. +

func (*Session) SendRequest

+
func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error)
+

SendRequest sends an out-of-band channel request on the SSH channel +underlying the session. +

func (*Session) Setenv

+
func (s *Session) Setenv(name, value string) error
+

Setenv sets an environment variable that will be applied to any +command executed by Shell or Run. +

func (*Session) Shell

+
func (s *Session) Shell() error
+

Shell starts a login shell on the remote host. A Session only +accepts one call to Run, Start, Shell, Output, or CombinedOutput. +

func (*Session) Signal

+
func (s *Session) Signal(sig Signal) error
+

Signal sends the given signal to the remote process. +sig is one of the SIG* constants. +

func (*Session) Start

+
func (s *Session) Start(cmd string) error
+

Start runs cmd on the remote host. Typically, the remote +server passes cmd to the shell for interpretation. +A Session only accepts one call to Run, Start or Shell. +

func (*Session) StderrPipe

+
func (s *Session) StderrPipe() (io.Reader, error)
+

StderrPipe returns a pipe that will be connected to the +remote command's standard error when the command starts. +There is a fixed amount of buffering that is shared between +stdout and stderr streams. If the StderrPipe reader is +not serviced fast enough it may eventually cause the +remote command to block. +

func (*Session) StdinPipe

+
func (s *Session) StdinPipe() (io.WriteCloser, error)
+

StdinPipe returns a pipe that will be connected to the +remote command's standard input when the command starts. +

func (*Session) StdoutPipe

+
func (s *Session) StdoutPipe() (io.Reader, error)
+

StdoutPipe returns a pipe that will be connected to the +remote command's standard output when the command starts. +There is a fixed amount of buffering that is shared between +stdout and stderr streams. If the StdoutPipe reader is +not serviced fast enough it may eventually cause the +remote command to block. +

func (*Session) Wait

+
func (s *Session) Wait() error
+

Wait waits for the remote command to exit. +

The returned error is nil if the command runs, has no problems +copying stdin, stdout, and stderr, and exits with a zero exit +status. +

If the remote server does not send an exit status, an error of type +*ExitMissingError is returned. If the command completes +unsuccessfully or is interrupted by a signal, the error is of type +*ExitError. Other error types may be returned for I/O problems. +

func (*Session) WindowChange

+
func (s *Session) WindowChange(h, w int) error
+

WindowChange informs the remote host about a terminal window dimension change to h rows and w columns. +

type Signal

+
type Signal string
+
const (
+	SIGABRT Signal = "ABRT"
+	SIGALRM Signal = "ALRM"
+	SIGFPE  Signal = "FPE"
+	SIGHUP  Signal = "HUP"
+	SIGILL  Signal = "ILL"
+	SIGINT  Signal = "INT"
+	SIGKILL Signal = "KILL"
+	SIGPIPE Signal = "PIPE"
+	SIGQUIT Signal = "QUIT"
+	SIGSEGV Signal = "SEGV"
+	SIGTERM Signal = "TERM"
+	SIGUSR1 Signal = "USR1"
+	SIGUSR2 Signal = "USR2"
+)
+

POSIX signals as listed in RFC 4254 Section 6.10. +

type Signature

+
type Signature struct {
+	Format string
+	Blob   []byte
+	Rest   []byte `ssh:"rest"`
+}
+

Signature represents a cryptographic signature. +

type Signer

+
type Signer interface {
+	// PublicKey returns the associated PublicKey.
+	PublicKey() PublicKey
+
+	// Sign returns a signature for the given data. This method will hash the
+	// data appropriately first. The signature algorithm is expected to match
+	// the key format returned by the PublicKey.Type method (and not to be any
+	// alternative algorithm supported by the key format).
+	Sign(rand io.Reader, data []byte) (*Signature, error)
+
+	// SignWithAlgorithm is like Signer.Sign, but allows specifying a desired
+	// signing algorithm. Callers may pass an empty string for the algorithm in
+	// which case the AlgorithmSigner will use a default algorithm. This default
+	// doesn't currently control any behavior in this package.
+	SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
+
+	// Algorithms returns the available algorithms in preference order. The list
+	// must not be empty, and it must not include certificate types.
+	Algorithms() []string
+}
+

A Signer can create signatures that verify against a public key. +

Some Signers provided by this package also implement MultiAlgorithmSigner. +

func NewCertSigner

+
func NewCertSigner(cert *Certificate, signer Signer) (Signer, error)
+

NewCertSigner returns a Signer that signs with the given Certificate, whose +private key is held by signer. It returns an error if the public key in cert +doesn't match the key used by signer. +

func NewSigner

+
func NewSigner(signer crypto.Signer) (Signer, error)
+

NewSigner takes any crypto.Signer implementation and returns a corresponding +Signer interface. This can be used, for example, with keys kept in hardware +modules. +

func NewSignerWithAlgorithms

+
func NewSignerWithAlgorithms(signer Signer, algorithms []string) (Signer, error)
+

NewSignerWithAlgorithms returns a signer restricted to the specified +algorithms. The algorithms must be set in preference order. The list must not +be empty, and it must not include certificate types. An error is returned if +the specified algorithms are incompatible with the public key type. +

func ParsePrivateKey

+
func ParsePrivateKey(pemBytes []byte) (Signer, error)
+

ParsePrivateKey returns a Signer from a PEM encoded private key. It supports +the same keys as ParseRawPrivateKey. If the private key is encrypted, it +will return a PassphraseMissingError. +

func ParsePrivateKeyWithPassphrase

+
func ParsePrivateKeyWithPassphrase(pemBytes, passphrase []byte) (Signer, error)
+

ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private +key and passphrase. It supports the same keys as +ParseRawPrivateKeyWithPassphrase. +

type TerminalModes

+
type TerminalModes map[uint8]uint32
+

type Waitmsg

+
type Waitmsg struct {
+	// contains filtered or unexported fields
+}
+

Waitmsg stores the information about an exited remote command +as reported by Wait. +

func (Waitmsg) ExitStatus

+
func (w Waitmsg) ExitStatus() int
+

ExitStatus returns the exit status of the remote command. +

func (Waitmsg) Lang

+
func (w Waitmsg) Lang() string
+

Lang returns the language tag. See RFC 3066 +

func (Waitmsg) Msg

+
func (w Waitmsg) Msg() string
+

Msg returns the exit message given by the remote command +

func (Waitmsg) Signal

+
func (w Waitmsg) Signal() string
+

Signal returns the exit signal of the remote command if +it was terminated violently. +

func (Waitmsg) String

+
func (w Waitmsg) String() string
+

Directories

+ + + + + + + + + + + + +
agentPackage agent implements the ssh-agent protocol, and provides both a client and a server.
knownhostsPackage knownhosts implements a parser for the OpenSSH known_hosts host key database, and provides utility functions for writing OpenSSH compliant known_hosts files.
+
+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/index.html b/design/65269/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/index.html new file mode 100644 index 00000000..170f2d7b --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf/index.html @@ -0,0 +1,39 @@ + + + + + + + + + + + bcrypt_pbkdf + + + +

package bcrypt_pbkdf

+
import "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
+

Package bcrypt_pbkdf implements bcrypt_pbkdf(3) from OpenBSD. +

See https://flak.tedunangst.com/post/bcrypt-pbkdf and +https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libutil/bcrypt_pbkdf.c. +

Index

+

Functions

+

func Key

+
func Key(password, salt []byte, rounds, keyLen int) ([]byte, error)
+

Key derives a key from the password, salt and rounds count, returning a +[]byte of length keyLen that can be used as cryptographic key. +

+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/internal/index.html b/design/65269/golang.org/x/crypto/ssh/internal/index.html new file mode 100644 index 00000000..a3b61442 --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/internal/index.html @@ -0,0 +1,40 @@ + + + + + + + + + + + golang.org/x/crypto/ssh/internal + + + +

Directories

+ + + + + + + + + + + + +
bcrypt_pbkdfPackage bcrypt_pbkdf implements bcrypt_pbkdf(3) from OpenBSD.
testPackage test contains integration tests for the golang.org/x/crypto/ssh package.
+
+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/internal/test/index.html b/design/65269/golang.org/x/crypto/ssh/internal/test/index.html new file mode 100644 index 00000000..f08a0ac2 --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/internal/test/index.html @@ -0,0 +1,31 @@ + + + + + + + + + + + test + + + +

package test

+
import "golang.org/x/crypto/ssh/internal/test"
+

Package test contains integration tests for the +golang.org/x/crypto/ssh package. +

Index

+
+
+ + + diff --git a/design/65269/golang.org/x/crypto/ssh/knownhosts/index.html b/design/65269/golang.org/x/crypto/ssh/knownhosts/index.html new file mode 100644 index 00000000..dc74df4e --- /dev/null +++ b/design/65269/golang.org/x/crypto/ssh/knownhosts/index.html @@ -0,0 +1,105 @@ + + + + + + + + + + + knownhosts + + + +

package knownhosts

+
import "golang.org/x/crypto/ssh/knownhosts"
+

Package knownhosts implements a parser for the OpenSSH known_hosts +host key database, and provides utility functions for writing +OpenSSH compliant known_hosts files. +

Index

+

Functions

+

func HashHostname

+
func HashHostname(hostname string) string
+

HashHostname hashes the given hostname. The hostname is not +normalized before hashing. +

func Line

+
func Line(addresses []string, key ssh.PublicKey) string
+

Line returns a line to add append to the known_hosts files. +

func New

+
func New(files ...string) (ssh.HostKeyCallback, error)
+

New creates a host key callback from the given OpenSSH host key +files. The returned callback is for use in +ssh.ClientConfig.HostKeyCallback. By preference, the key check +operates on the hostname if available, i.e. if a server changes its +IP address, the host key check will still succeed, even though a +record of the new IP address is not available. +

func Normalize

+
func Normalize(address string) string
+

Normalize normalizes an address into the form used in known_hosts +

Types

+

type KeyError

+
type KeyError struct {
+	// Want holds the accepted host keys. For each key algorithm,
+	// there can be one hostkey.  If Want is empty, the host is
+	// unknown. If Want is non-empty, there was a mismatch, which
+	// can signify a MITM attack.
+	Want []KnownKey
+}
+

KeyError is returned if we did not find the key in the host key +database, or there was a mismatch. Typically, in batch +applications, this should be interpreted as failure. Interactive +applications can offer an interactive prompt to the user. +

func (*KeyError) Error

+
func (u *KeyError) Error() string
+

type KnownKey

+
type KnownKey struct {
+	Key      ssh.PublicKey
+	Filename string
+	Line     int
+}
+

KnownKey represents a key declared in a known_hosts file. +

func (*KnownKey) String

+
func (k *KnownKey) String() string
+

type RevokedError

+
type RevokedError struct {
+	Revoked KnownKey
+}
+

RevokedError is returned if we found a key that was revoked. +

func (*RevokedError) Error

+
func (r *RevokedError) Error() string
+
+
+ + + diff --git a/design/65269/golang.org/x/index.html b/design/65269/golang.org/x/index.html new file mode 100644 index 00000000..6bef2c16 --- /dev/null +++ b/design/65269/golang.org/x/index.html @@ -0,0 +1,36 @@ + + + + + + + + + + + golang.org/x + + + +

Directories

+ + + + + + + + +
crypto/sshPackage ssh implements an SSH client and server.
+
+
+ + + diff --git a/design/65269/index.html b/design/65269/index.html new file mode 100644 index 00000000..fce8bfaf --- /dev/null +++ b/design/65269/index.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + +

Directories

+ + + + + + + + +
golang.org/x/crypto/sshPackage ssh implements an SSH client and server.
+
+
+ + +