Documentation
¶
Overview ¶
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-00
Index ¶
- Variables
- func ForwardToAgent(client *ssh.Client, keyring Agent) error
- func ForwardToRemote(client *ssh.Client, addr string) error
- func RequestAgentForwarding(session *ssh.Session) error
- func ServeAgent(agent Agent, c io.ReadWriter) error
- type AddedKey
- type Agent
- type ConstraintExtension
- type ExtendedAgent
- type Key
- type SignatureFlags
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
ErrExtensionUnsupported indicates that an extension defined in [PROTOCOL.agent] section 4.7 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 ¶
ForwardToAgent routes authentication requests to the given keyring.
func ForwardToRemote ¶
ForwardToRemote routes authentication requests to the ssh-agent process serving on the given unix socket.
func RequestAgentForwarding ¶
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 interface{}
// 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 2.6.2.
Sign(key ssh.PublicKey, data []byte) (*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.
The returned Agent only supports the "lifetime" constraint.
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
// SignWithFlags signs like Sign, but allows for additional flags to be sent/received
SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
// 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)
}
func NewClient ¶
func NewClient(rw io.ReadWriter) ExtendedAgent
NewClient returns an Agent that talks to an ssh-agent process over the given connection.
If rw also implements io.Closer (like *net.UnixConn and ssh.Channel do), the returned client pipelines concurrent requests over the connection: callers can issue Sign and other operations from multiple goroutines and they will be written to the agent as soon as the wire is available, rather than waiting for the previous responses. The ssh-agent protocol still requires responses to be returned in request order, so a slow request delays subsequent responses on the same connection (head-of-line blocking).
Pipelining requires io.Closer because, on a Write error, the background reader goroutine must be unblocked by closing the underlying connection. When rw does not implement io.Closer this is not possible, so NewClient falls back to fully serializing each request: a single in-flight call at a time.
Example ¶
package main
import (
"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),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
sshc, err := ssh.Dial("tcp", "localhost:22", config)
if err != nil {
log.Fatal(err)
}
// Use sshc...
sshc.Close()
}
Output:
type Key ¶
Key represents a protocol 2 public key as defined in [PROTOCOL.agent], section 2.5.2.
type SignatureFlags ¶
type SignatureFlags uint32
SignatureFlags represent additional flags that can be passed to the signature requests an defined in [PROTOCOL.agent] section 4.5.1.
const ( SignatureFlagReserved SignatureFlags = 1 << iota SignatureFlagRsaSha256 SignatureFlagRsaSha512 )
SignatureFlag values as defined in [PROTOCOL.agent] section 5.3.