sfab

package module
v1.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2020 License: MIT Imports: 15 Imported by: 0

README

sFAB - An SSH Fabric for Distributed Agent Orchestration

Read our godoc!

Welcome to sFAB, a utility library for stringing together arbitrary agents, running across all types of IP networks, to form an ad hoc execution fabric, all securely over SSH!

That's quite a mouthful. We can do better.

sFAB lets you write programs that communicate securely, in a hub-and-spoke topology. The following concepts are key:

  • An Agent is a process running somewhere on the network, and performs tasks when asked to.

  • A Hub is a process running somewhere on the network.

  • Each Agent connects to one or more Hubs.

  • Commands are sent by consumers, via a Hub, and bound for a specific Agent.

How to Run a Hub

Before you can get a lot of mileage out of sFAB, we're going to need to spin up a Hub:

import (
  "github.com/jhunt/go-sfab"
)

func main() {
  hub := &Hub{
    Bind:        "*:4000",
    HostKeyFile: "host_key",
  }

  hub.AuthorizeKeys("authorized_keys")
  hub.ListenAndServe()
}

This will spin up a Hub (an SSH server) bound to all interfaces on TCP port 4000, using the contents of the host_key file as its private key, and allowing anyone with a username + keypair listed in the authorized_keys file to connect (as an Agent).

How Agents Connect to the Hub

Agents connect to a Hub via the Connect() method:

import (
  "io"

  "github.com/jhunt/go-sfab"
)

func main() {
  agent := &sfab.Agent{
    Identity:       "bob@postgres.ql",
    PrivateKeyFile: "id_rsa",
  }

  handler := func(msg []byte, stdout, stderr io.Writer) (int, error) {

    // ... do something useful here ...

    return 0, nil
  }

  agent.Connect("tcp4", "hub.fqdn:4000", handler)
}

This Agent's Connect() method will present the Identity of "bob@postgres.ql" to the remote Hub, along with the private key stored in the id_rsa file.

Agent / Hub Communication

The authentication handshake (part of the SSH-USERAUTH protocol, handled by lower layers of this library) identifies the agent. The Hub must have pre-authorized the use of our private key (from id_rsa) as the identity "bob@postgres.ql".

The Hub records this information, and associates the agent name with the SSH connection. The next time one of its callers asks for something to be executed against an agent named "bob@postgres.ql", the Hub will relay that to this Agent, by doing the following:

  1. Opening an SSH "session" channel (per section 5.1 of RFC-4254)
  2. Issuing an "exec" request (per section 6.5 of RFC-4254)
  3. Copying output from the remote agent back to the caller, by way of the responses (Go) channel.

Running Something on the Fabric

To actually use this fabric, we need a third party, our client. A client is just the Go code that calls into the Hub and asks for things to be run against remote agents:

Agent Command Execution

Here's an adjusted Hub implementation that attempts to re-reconcile bob every 30 seconds:

import (
  "fmt"
  "time"
  "os"

  "github.com/jhunt/go-sfab"
)

func main() {
  hub := &sfab.Hub{
    Bind:        "*:4000",
    HostKeyFile: "host_key",
  }

  hub.AuthorizeKeys("authorized_keys")

  go func() {
    t := time.NewTicker(30 * time.Second)
    for range t.C {

      /* every 30 seconds: */
      fmt.Fprintf(os.Stderr, "running reconciliation...\n")
      reply, _ := hub.Send("bob@postgres.ql", []byte("reconcile(x)"))

      for msg := range reply {
        if msg.IsStdout() || msg.IsStderr() {
          fmt.Printf("%s\n", msg.Text())

        } else if msg.IsError() {
          fmt.Fprintf(os.Stderr, "oops: %s\n", msg.Error())

        } else if msg.IsExit() {
          fmt.Fprintf(os.Stderr, "command exited %d\n", msg.ExitCode())
        }
      }
    }
  }()
  hub.ListenAndServe()
}

Now, our Hub will boot up, listen on *:4000 as before (same host keys, same authorizations), and then every 30 seconds will tell bob to reconcile.

Next, let's modify the Agent implementation to do something:

import (
  "io"
  "fmt"

  "github.com/jhunt/go-sfab"
)

func main() {
  agent := &sfab.Agent{
    Identity:       "bob@postgres.ql",
    PrivateKeyFile: "id_rsa",
  }

  handler := func(msg []byte, stdout, stderr io.Writer) (int, error) {

    if string(msg) == "reconcile(x)" {
      fmt.Fprintf(stdout, "BEGIN RECONCILIATION\n")
      reconciliation()
      fmt.Fprintf(stdout, "END RECONCILIATION\n")
      return 0, nil
    }

    fmt.Fprintf(stderr, "I'm sorry; I don't know how to '%s'\n", string(msg))
    return 1, nil
  }

  agent.Connect("tcp4", "hub.fqdn:4000", handler)
}

When Agents Aren't Available

So what happens if an agent is unavailable?

If it's a temporary availability issue, and the Agent is maintaining its connection to the Hub, the messages sent by Send() will be queued in the goroutine.

If the Agent has left (or has not yet connected), the Hub will respond to the caller accordingly:

No Such Agent

Halting an Agent

At times, you may want an in-line method of terminating an Agent on behalf of a Client, through the Hub. The handler function that gets passed to the Agent's Connect() method can return an error to signal this condition. The Agent will process the error (logging it, mostly), and then break out of its main event loop, effectively shutting down.

Note that this error will not propagate to the caller of Connect(); the error returned by that function is intended solely to signal a problem handling the connection, and exiting early is not problematic.

Here's an example:

handler := func(msg []byte, stdout, stderr io.Writer) (int, error) {

  if string(msg) == "EXEUNT" {
    fmt.Fprintf(stdout, "exiting...\n")
    return 0, fmt.Errorf("exit requested")
  }

  // ... other, more useful work here ...

  return 0, nil
}

agent.Connect("tcp4", "hub.fqdn:4000", handler)

The Example SFAB Ping System

This repository ships with a fully-functional, if a touch silly example implementation. To build it:

$ go build ./example/demo

That will put a multi-call binary, named sfab in your current working directory (the root of the codebase). The example system consists of a hub, which binds a loopback SSH server and an HTTP control module (also on loopback), an agent that connects to the SSH port and waits for PINGs to PONG back, and a handful of CLI sub-commands to interrogate the internal state of the hub and to poke the agents.

Screenshot of Example SFAB Ping System

It also ships with an envrc that sets all the environment variables to make running a single hub + agent trivial:

$ source example/demo/envrc

To play along at home, run the following commands, in different terminal windows or tmux panes:

$ source example/demo/envrc && ./demo hub
$ source example/demo/envrc && ./demo agent test-agent
$ source example/demo/envrc && watch ./demo keys

Then, you can ping the test-agent agent:

$ source example/demo/envrc
$ ./demo ping test-agent

This should fail, since we haven't authorized the test-agent's key; we don't yet trust that agent. To establish that trust:

$ source example/demo/envrc  # if you hven't already...
$ ./demo authz test-agent

You'll notice that the watch command we ran will list the key as authorized now, and the agent as "known". Test it out by pinging the agent again:

$ ./demo ping test-agent

This time, you should see some output from the hub, detailing the PONG response from the agent, and you should see some chatter on the agent pane / terminal about the receipt of a PING.

You can deauthorize the test-agent by running:

$ ./demo deauthz test-agent

You can also get a list of all the PING / PONG exchanges by running:

$ ./demo responses

To play with it some more, refer to the built-in help:

$ ./demo --help

To play with it some more, refer to the built-in help:

$ ./demo --help demo - An example implementation of SSH-Fabric

COMMANDS

  hub             Run an SFAB hub

    -b, --bind    What IP:port to bind the SSH endpoint on. ($SFAB_HUB_BIND)
    -l, --listen  What IP:port to bind the HTTP API on. ($SFAB_HUB_LISTEN)
    -k, --key     Path to the private SSH host key. ($SFAB_HUB_HOST_KEY)

  agent NAME      Run an SFAB agent

    -H, --hub     What IP:port of the hub to connect to. ($SFAB_HUB)
    -k, --key     Path to the agent's private SSH key. ($SFAB_AGENT_KEY)

  keys            List known agents, their keys, and authorizations.

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)

  agents          List authorized agents, by name.

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)

  responses       Dump the responses from all agents.

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)

  auth AGENT      Authorize an agent (by name and key)

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)
    -f            SHA256 key fingerprint. ($SFAB_AGENT_FINGERPRINT)

  deauth AGENT    Deauthorize an agent (by name and key)

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)
    -f            SHA256 key fingerprint.

  ping AGENT      Ping an agent, by identity.
                  Authorized agents should PONG us back.
                  Unauthorized agents should not.

    -a, --api     The full URL of the hub HTTP API. ($SFAB_HUB)

Go ahead! Try spinning up some other agents, either reusing the existing example/demo/agent_key, or generating a new one.

Generating SFAB RSA Keys

The state of the art in generating RSA private and public keys is a bit saddening, to say the least. To make life easier for ourselves, and others, we wrote a small helper utility named sfab, and packaged it up in this repository.

You can use it to generate a keypair like this:

$ sfab keygen
generating a 2048-bit rsa private key to standard output...
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA2jRmGj8AdGB+QhwPcXPltij33NnP/Q5tyYaE+n7WPsrnWQzh
8IeCVX2Jkxnjn7uT2y4kNDEbY2P8DewWMlgTCyulIlZgAFxNGnrvRECkH6wVRlWM
nlx3UlQgNExIIfDg0Z8A7DOChFD1GGe/twN37bgtempHUT2TDPGjK0jpMmcGbI9q
rvnidoeipXifgpSROOxs8qnWA8BVgdqUqGiA5FWRV2yTXO9G07jsP4Vf1Vdu87Eb
XMlRxAcFQOtf+TjbPGtSkDeyJSkjsHmT/+JYvJQlKL4gg8K0+Uw2/aemLetvwsCa
Po9RO5XguSDGC8ecqE+758Rv6Jiz26UFcN21mwIDAQABAoIBADU1Y9Rwmn7GbONY
Dlf/B/vkf5SGNJajpxfmSpT+yuWi+skXQB00Hg8UvrB1NrFjv0lKNh/X/bK91d/b
OwppubMWoH0ADQbpew/C/IdrL9pdTw7Im7KeH6aexv+ZvlkwcNIM8N+oK+JyD2I1
ImXRXbRFHinksqFX33zfi4dpjxvv2eSImeNRI4mY4e9zNCYzMgH/51N9MVQNwEFu
E2ExJhyPnGa2Pld1FewYczvhIkPPhB6UZsw0M9FicrZfFN5GJwVr+P121S/FMDIm
N0j2d7utkqfh639sOM7Y4na5T8ztn/UuC0xHCnI2eDpXUhT+47oERYqQYifyz1by
nRzIXZkCgYEA20Bx474++BJ6jmNyLzxnxMLDaFv1oAOaN8JrVhzQ++sDQ92M+UKX
BL4WqCw4LYwE+UIcsRgAw/GXTuqLB7L4uAfujyx/uPR1o4v9qE9Bbx23dgKyE8fa
JJzAEw8AzhQ2Rxnlof28EqMySKZ5hDuG+/mF8czzV7Bg6ASpCmszpkUCgYEA/scH
A31UI86mpRzTpg89gNENt17mwP5isfgKXAxL1zhgdszLKSxwfSTMQzSAypieqdFq
zsMoULtpq+lzRoUHXIMLdD+hcLLRVBnPDWxT0WGmAYLaIN1L7RxMz+sx+hrexy/d
F7WdS//G8/YEwNr0IBFKC3sj7vceAEIGLweQGl8CgYANjW88x5620NWYhZciSlaT
uAJCKiUFrhNTTQY7fW6Bk+ZQxJoZMj+JgIKABLO4RQZtreN+IuCxO/bBXwgXAr4T
ChJ58SM3+Ixo/pdAVx3RU4HqnoIJbPoT90h8JRpEosU0Ieqq17dPY50RFpE+Vjui
yaq45pVIH1PuI5ObXEMGDQKBgH+H1r3rbUOjPI1nuZXlCQ9DFc+VJIK0k2isVVsw
GIJ5yOeOqZ9JjGFIGysEy/cdoEY2+nTXAYYv2bxg1gAVtNKdEg21kDUefWPQq3D3
qZkkfPlCq7dJrqjYwqZM/65G5v7juZMVqNATD16s5kI3F345yUun7xu9/Pl6EyhV
a8+XAoGBAJmeUEDVGv9uiICM8abfl7wyxekD3pdfajov5gWj5PKYW7tLBRfbbmcy
UdM0PnqUgKgICqhUfMcRlc1+anH8JPYQnfDgTzimKei4Y07KJgeyrZyi0nojk441
UGr9V8J7AypffH88uQt3msGhkP1wPA60PIhoIkzAYjPRrSda3PU7
-----END RSA PRIVATE KEY-----

The sfab key command lets you validate and optionally print the private and public key components of a given key file:

$ sfab keygen > test.pem
$ sfab key -q --private --public test.pem
$ echo $?
0
$ sfab key --private --public test.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtgQJei7jzcr4HTNydR6GOVdFr/c2zdnhCku3BHLogliV1hNA
9oUqajCNUvpkDRMxSWCIdiQdhms5N/o+nBJQgEj259eiKdcsrBKGk7SjDXgdjBTD
yTgx550/3O2A0qACk14Xf2xIKSidOByIBFFjbh5oQq0nTZnq8Sa5lZ6lx4kzvHy3
Qhg+mbv5ocdrgpNf6Wi1aZTzm39bvwfh5UvZxXwgBP4s4Nf8LAbatpgy8S4jt5kg
PsCrD0Nw1tQTrgtgrQO3KjPwizdnUwkSS3jAX40hKW7d229IS/0hrHBn4Tc0HaWA
F2fncwYD8BwHFI1ZmwMMWHXzdNFvDC/CInALgQIDAQABAoIBAQCI7SSMDDZHnIdm
wEs8nEfxXxwHsnxCA8Rt0GPQLvmHBUlDEJzwV2F/HSNT5xXZWDTdtIc0CRtFAei7
mb/7/TMeLsW/x9Iwk7UBhbZD68I14cSomv5mo6W3Te67LtiYmtMVNdWLgFQQ0oLj
sWMJjIi2UJ2yezxz/nRmEfHPAthH8EQCoxR1BROdlaaeyfz6WVfFp27NzO3Uq9ZZ
8DJUPuzmHiJmQcopgtYknzBXoPC7TgKBL1ozMohbhQo7wJQTZsrxjMeBuVDSknZf
uXbrf1x+SfcME4fENxI6NdNQL23LFHuE9LMuapIURPFpDVDJVGs9Van2g5KiVYtt
tHl/doqBAoGBAMycXLDWulilAHKkDNHAarTk8cgL7F3kweyhPF2gIt2gudTKW5WL
iUuPdgbDDvdkcx2wbKYwpzWh0EHCGoNL770RT7gOMVf9EcjzGZPfid7tV7lTKrSF
rT6PNIM3AHVuOoiNe/Su/8jf/vhAx1xYzD4eAb1IprpiDNU/ww17PlipAoGBAOO6
6FB8CX8MP3ejzWC7mAku5tVUIGNGrWztI3c0HKGaGpLnc1OzFBmDVW5nHTodhj+D
VlTXxZeKunJnEtA4QyxDJuFH+dFZ7LQ+mcCMUojv0qiI/8GnGV+VV1zookslA+J2
BayVAEcKSyw4lc1lJdQKfD1KycPOgbEN7FSs7SsZAoGBAJc5NZSJj352rQcN0l+w
G+2IjF4y2cZnjSBL5qa39SziXpYz3U6Aq7+Jz0yeG9CikCYwP4zwxpRv/NCtGhVS
KWUOh2HcM4H5i4x+h7fv7iEyP7S8EAMODYYAJ2qXWyJvjplgIG4ubOPVEcf/cJkD
FCkQ2m5AZOG6GAwALTQok2LJAoGAeJZNU+kKYkMLDIApTZKoZBx40Pe2GrufM7Gy
ePmYO55NegKjF+wYtZXM1CQ33sEpiFI0kcKb963XIqMf3DAeqb55RQuG5mt2os8P
PV1QiwBrDSFWRKYGEo4S77ijz6SvFOioAoX32qZBvg3MjTGFQW238gC7LDsag7W6
se/LumkCgYBt0+ylBg9zh0QIKFXvBecRVtIgvjV57mWvFQkB/4/0F3P2Hz87Jja5
GLr9neUNVcdD7RRL10/m/V0uJXVrBO7GinhsEFfuzNdMBM+7USdnOv2r1rQwJfsO
OlNmor/8YUVY8/YIDU6cZ7dHfxQBWjoKj8dt8jMMJGxqsJ96HsgtFw==
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAtgQJei7jzcr4HTNydR6GOVdFr/c2zdnhCku3BHLogliV1hNA9oUq
ajCNUvpkDRMxSWCIdiQdhms5N/o+nBJQgEj259eiKdcsrBKGk7SjDXgdjBTDyTgx
550/3O2A0qACk14Xf2xIKSidOByIBFFjbh5oQq0nTZnq8Sa5lZ6lx4kzvHy3Qhg+
mbv5ocdrgpNf6Wi1aZTzm39bvwfh5UvZxXwgBP4s4Nf8LAbatpgy8S4jt5kgPsCr
D0Nw1tQTrgtgrQO3KjPwizdnUwkSS3jAX40hKW7d229IS/0hrHBn4Tc0HaWAF2fn
cwYD8BwHFI1ZmwMMWHXzdNFvDC/CInALgQIDAQAB
-----END RSA PUBLIC KEY-----

With --quiet (-q) mode, you can even validate a bunch of keys in a directory:

$ sfab key -q ~/my/keys/*.pem

... or via a find + xargs shell pipeline:

$ find / -name 'rsa*.pem' | xargs sfab key -q

Contributing

This code is licensed MIT. Enjoy.

If you find a bug, please raise a Github Issue first, before submitting a PR.

Happy Hacking!

Documentation

Index

Constants

View Source
const (
	UnknownDisposition disposition = 0
	Authorized                     = 1
	NotAuthorized                  = 2

	PublicKeyExtensionName = "sfab-pubkey"

	Wildcard = "*"
)
View Source
const DefaultKeepAlive time.Duration = 60 * time.Second
View Source
const DefaultTimeout time.Duration = 30 * time.Second

DefaultTimeout will be used as a fallback, should an Agent not set its Timeout attribute to a non-zero connect timeout.

Variables

View Source
var (
	AgentNotFoundError      = errors.New("agent not found")
	AgentNotAuthorizedError = errors.New("agent not authorized")
)

Functions

func IsAgentNotAvailableError

func IsAgentNotAvailableError(e error) bool

Types

type Agent

type Agent struct {
	// Name of this agent, which will be sent to any Hub this Agent connects
	// to, and used to validate authorization (along with its private key).
	//
	Identity string

	// Private Key to use for connecting to upstream sFAB Hubs.
	//
	PrivateKey *Key

	// How long to wait for an upstream Hub to connect.
	//
	Timeout time.Duration
	// contains filtered or unexported fields
}

An Agent represents a client that connects to a Hub over SSH, and awaits instructions on what to do. Each Agent has an identity (its name and private key).

func (*Agent) AcceptAnyHostKey

func (a *Agent) AcceptAnyHostKey()

Instruct the Agent to (insecurely) accept any host key presented by the Hub, when connecting. This is a terrible idea in production, but can be useful in development or debugging scenarios.

Note: calling this function will obliterate any keys authorized by the AuthorizeKey() method.

func (*Agent) AuthorizeKey

func (a *Agent) AuthorizeKey(host string, key *Key)

Authorize a specific Hub Host Key, which will be accepted from any Hub with the name or IP address given as `host`.

func (*Agent) Connect

func (a *Agent) Connect(proto, host string, handler Handler) error

Connect to a remote sFAB Hub, using the given protocol (i.e. "tcp4" or "tcp6"), and respond to execution requests with the passed Handler.

This method will block, so if the caller wishes to do other work, this is best run in a goroutine.

type AgentCallback added in v1.1.2

type AgentCallback func(string, Key)

type Authorization

type Authorization struct {
	PublicKey      *Key
	Identity       string
	KeyFingerprint string
	Authorized     bool
	Known          bool
}

type Handler

type Handler func(payload []byte, stdout io.Writer, stderr io.Writer) (rc int, err error)

A Handler is the primary workhorse of the Hub + Agent distributed orchestration engine.

Each Handler will be passed the opaque message payload from the Hub as its first argument (a slice of bytes, arbitrarily long), and two output streams: one for standard output and the other for standard error.

A Handler function returns two values: a Unix-style integer exit code, and an error that (if non-nil) will terminate the Agent's main loop.

type Hub

type Hub struct {
	// The IP address (or hostname / FQDN) and TCP port to
	// bind and listen on for incoming SSH connections from
	// sFAB Agents.
	//
	Bind string

	// Whether or not to allow connecting agents to finish
	// connecting before their private keys have been authorized
	// by the hub (or its operators).
	//
	AllowUnauthorizedAgents bool

	// Which IP protocol (tcp4 or tcp6) to use for binding
	// the server component of this sFAB Hub.
	//
	IPProto string

	// Private Key to use for the server component of this Hub.
	//
	HostKey *Key

	// How frequently to send KeepAlive messages to connected
	// agents, to keep their TCP transport channels open.
	//
	// By default, no KeepAlives are sent.
	//
	KeepAlive time.Duration

	// An optional function to be called when a new agent
	// registers with the hub (authorized or not).
	//
	OnConnect AgentCallback

	// An optional function to be called when a registered
	// agent (authorized or not) deregisters from the hub,
	// or is forcibly deregistered after a missed heartbeat.
	//
	OnDisconnect AgentCallback
	// contains filtered or unexported fields
}

A Hub represents a server from whence jobs to execute are dispatched. sFAB Agents connect _to_ a Hub, and await instructions.

func (*Hub) Agents

func (h *Hub) Agents() []string

Agents() returns a list of all registered (and current!) Agent names, to allow customers to blast out messages to _everyone_ if they so desire.

func (*Hub) Authorizations

func (h *Hub) Authorizations() []Authorization

func (*Hub) AuthorizeKey

func (h *Hub) AuthorizeKey(agent string, key *Key)

AuthorizeKey tells the Hub to start trusting a given SSH key pair, given the public component, for a named agent.

This can be called dynamically, long after a call to Listen(), or before.

func (*Hub) Await

func (h *Hub) Await(agent string) chan int

func (*Hub) DeauthorizeKey

func (h *Hub) DeauthorizeKey(agent string, key *Key)

DeauthorizeKey tells the Hub to stop trusting a given SSH key pair, given the public component, for a named agent.

This can be called dynamically, long after a call to Listen(), or before.

func (*Hub) IgnoreReplies

func (h *Hub) IgnoreReplies(ch chan *Response)

IgnoreReplies takes a response channel from a call to Send() and discards all of the responses that are sent across.

It's perfect for a goroutine!

func (*Hub) KnowsAgent

func (h *Hub) KnowsAgent(agent string) bool

KnowsAgent checks the Hub's agent directory to see if a named agent has registered with this Hub.

func (*Hub) Listen

func (h *Hub) Listen() error

Listen binds a network socket for the Hub.

func (*Hub) ListenAndServe

func (h *Hub) ListenAndServe() error

ListenAndServe combines both the Listen() and Serve() methods into a convenient helper method that runs both, serially, and returns whichever error pops up first.

You probably want to run this in the main goroutine, much like net/http's ListenAndServe().

func (*Hub) Send

func (h *Hub) Send(agent string, message []byte, timeout time.Duration) (chan *Response, error)

Send a message to an agent (by name). Returns an error if the named agent is not currently registered with this Hub.

If an Agent is found, Responses (including output and the ultimate exit code) will be sent via the returned channel.

func (*Hub) Serve

func (h *Hub) Serve() error

Serve handls inbound cnnections on the listening socket, and services those agents, distributing messages via a session channel and an exec request, each.

It is the caller's responsibility to call Listen() before invoking this method, or to dispense with both and just use ListenAndServe().

type Key added in v1.1.0

type Key struct {
	// contains filtered or unexported fields
}

func GenerateKey added in v1.1.0

func GenerateKey(bits int) (*Key, error)

func ParseKey added in v1.1.0

func ParseKey(b []byte) (*Key, error)

func ParseKeyFromFile added in v1.1.0

func ParseKeyFromFile(f string) (*Key, error)

func ParseKeyFromString added in v1.1.0

func ParseKeyFromString(s string) (*Key, error)

func (Key) Encode added in v1.1.0

func (k Key) Encode() []byte

func (Key) EncodeString added in v1.1.0

func (k Key) EncodeString() string

func (Key) Fingerprint added in v1.1.3

func (k Key) Fingerprint() string

func (Key) IsPrivateKey added in v1.1.0

func (k Key) IsPrivateKey() bool

func (Key) IsPublicKey added in v1.1.0

func (k Key) IsPublicKey() bool

func (Key) Private added in v1.1.0

func (k Key) Private() *Key

func (Key) Public added in v1.1.0

func (k Key) Public() *Key

type KeyMaster

type KeyMaster struct {
	// contains filtered or unexported fields
}

A KeyMaster handles the specifics of tracking which SSH key pairs are acceptable for which subjects (either hostnames, IPs, or agent names). It provides primitives for authorizing and deauthorizing these key pairs, and sports some helper methods for integrating with the rest of the x/crypto/ssh library.

func (KeyMaster) Authorizations

func (m KeyMaster) Authorizations() []Authorization

func (*KeyMaster) Authorize

func (m *KeyMaster) Authorize(key *Key, subjects ...string)

Authorize a key pair for one or more subjects (either hostnames, IP addresses, or agent names).

func (*KeyMaster) Authorized

func (m *KeyMaster) Authorized(subject string, key *Key) bool

Checks whether or not a public key has been pre-authorized for a given subject (either a hostname, IP address, or agent name).

func (*KeyMaster) Deauthorize

func (m *KeyMaster) Deauthorize(key *Key, subjects ...string)

Deauthorize a key pair for one or more subjects (either hostnames, IP addresses, or agent names).

type Message

type Message struct {
	// contains filtered or unexported fields
}

type Response

type Response struct {
	// contains filtered or unexported fields
}

func (Response) Error

func (r Response) Error() error

func (Response) ExitCode

func (r Response) ExitCode() int

func (Response) IsError

func (r Response) IsError() bool

func (Response) IsExit

func (r Response) IsExit() bool

func (Response) IsStderr

func (r Response) IsStderr() bool

func (Response) IsStdout

func (r Response) IsStdout() bool

func (Response) Text

func (r Response) Text() string

Directories

Path Synopsis
cmd
example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL