ssh

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2023 License: Apache-2.0 Imports: 8 Imported by: 0

README

Simplified pure Go secure shell (SSH) multi-host client

GoDoc License

Testable example

Testable runasany example

This directory also contains a testable example implementation of ssh.RunOnAny as the command runonany that can be used to validate this specific functionality interactively. This example requires the podman container engine and command to be installed on the local system.

Here are the steps to conduct an interactive test. In this way, one can monitor the status of a given server and bring it up or down on demand and note how this affects the ssh.Controller (inside of runonany).

A note about build script

The build bash script is designed to be used with simple bash tab completion. First add a build script like the following in your PATH.

#!/bin/sh
exec ./build "$@"

If you prefer you can do the above with ./build instead and avoid the script. This just avoids the anti-pattern of adding ./ to your PATH.

Then add the following to ~/.bashrc.

complete -C build build
Build the SSH server image

The testdata/server directory contains an ssh-server (ubuntu:latest) image used to bring up SSH servers on different ports. This directory contains everything for the ssh-client container image.

build server
Start up the three SSH server containers

Then start the three servers listening on ports 2221-2223.

build start-ssh-servers
Set the RUNONANY_TARGET environment variable

The containers all share the same underlying host (and IP address) but they don't know about it. We use the RUNONANY_TARGET environment variable to communicate this to the running containers.

export RUNONANY_TARGET=192.168.1.6
Check the servers by running ssh from each client

Confirm server SSH connection is working with ssh. First you will need to note the host IP of the podman container engine and export it. (This can be obtained any number of ways.)

Now check the servers (or just do individual):

build check-servers

You should see the ssh hostname output of each command.

Warning: Permanently added '[192.168.1.6]:2221' (ED25519) to the list of known hosts.
ssh-server1
Warning: Permanently added '[192.168.1.6]:2222' (ED25519) to the list of known hosts.
ssh-server2
Warning: Permanently added '[192.168.1.6]:2223' (ED25519) to the list of known hosts.
ssh-server3

Note that the primary distinction is the port number. These servers all share the same user and credentials. They even share the same authorized_hosts key (which we ignore here deliberately for testing).

Build and run runonany Go binary and client container

The runonany binary is a simple program that encapsulates an ssh.Controller configured in the runonany.yaml YAML file.

build client
build watch-client-runonany

This will update every two seconds.

Interactively stop and start SSH server containers

The containerized ssh-server images can be stopped and started while monitoring the live status using commands similar to the following:

build stop-ssh-server 2
build start-ssh-server 2
build stop-ssh-servers
build start-ssh-servers

It is useful to do these commands from one TMUX pane while running build watch-client-runonany from another to see the change in ssh.Controller.Clients status.

Here are some things to validate:

  • Random servers selected are between 1-3.
  • Stop one server and note random selected no longer include.
  • Stop two servers and not only single server selected.
  • Stop all servers and not only the ERR section returned.
  • Start one server after stopping and note recovery.
  • Start two servers and note recovery.
  • Start all servers and note recovery.

Documentation

Overview

Package ssh is simplified encapsulation of the standard x/crypto/ssh package with reasonable defaults and multi-client controller that can coordinate operations on multiple targets.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultPort = 22

DefaultPort for SSH connetions.

View Source
var DefaultTCPTimeout = 300 * time.Second

DefaultTCPTimeout is the default number of seconds to wait to complete a TCP connection.

Functions

This section is empty.

Types

type AllUnavailable added in v0.3.0

type AllUnavailable struct{}

func (AllUnavailable) Error added in v0.3.0

func (AllUnavailable) Error() string

type Client added in v0.3.0

type Client struct {

	// Host contains the host name or IP address along with any
	// authorization credentials and other data that would normally be
	// contained in authorized_hosts.
	Host *Host

	// Port is the port the ssh server is listening on on target server.
	// If unset DefaultPort is used.
	Port int

	// User contains the name of the user on target server and contains
	// the PEM private key authentication data as well.
	User *User

	// Timeout is the default number of seconds to wait to complete a TCP
	// connection. If unset DefaultTCPTimeout is used.
	Timeout time.Duration

	// Comment allows information comments about a specific client
	// connection to be persisted with the configuration data.
	Comment string
	// contains filtered or unexported fields
}

Client encapsulates an internal ssh.Client and associates a single user, host, and port number to target for specific ssh server connection adding a Connect method which implicitly dials up a connection setting Connected() and internally caching the client as SSHClient(). Client may be safely marshaled to/from YAML/JSON directly.

Example (From_YAMLJSON)
var err error

// YAML references are supported.

yml := []byte(`
immauser: &auser
  name: someuser
  key: somethingsecret

host:
  addr: host1

user: *auser

# timeout and port set to defaults when first used if zero

`)

client := new(ssh.Client)
err = yaml.Unmarshal(yml, client)
if err != nil {
	fmt.Println(err)
}
fmt.Println(client.User.Name)
fmt.Println(client.Host.Addr)
fmt.Println(client.Port)   // 0
fmt.Println(client.Addr()) // port added ...
fmt.Println(client.Port)   // but not set
Output:

someuser
host1
0
host1:22
0

func (Client) Addr added in v0.3.0

func (c Client) Addr() string

Addr returns network address suitable for use in TCP/IP connection strings. If the Port and Host are zero values returns empty host, colon, and DefaultPort (without setting it).

func (*Client) Connect added in v0.3.0

func (c *Client) Connect() error

Connect creates a new ssh.Client using the Client.Addr and caches it internally (see [SSHClient]). If [Client.Timeout] is zero uses ssh.DefaultTCPTimeout. If [Client.Port] is zero uses ssh.DefaultPort. Always reinitializes a new connection even if [Connected] is true. Also see User.Signer and Host.KeyCallback. If an attempted connection fails sets Client.Connected to false and assigns and returns [LastError].

func (*Client) Connected added in v0.3.0

func (c *Client) Connected() bool

Connected returns the last connection state of the internal SSH client. This is set to true on Connect. This does not guarantee that the current connection is still valid, just the last attempt.

func (Client) Dest added in v0.3.0

func (c Client) Dest() string

Dest returns the Addr with the [User.Name] prepended with an at (@) sign (if assigned).

func (*Client) LastError added in v0.3.0

func (c *Client) LastError() error

LastError returns the last error (if any) from an attempt to Connect. When set Connected is guaranteed to return false.

func (*Client) Run added in v0.3.0

func (c *Client) Run(cmd string, stdin []byte) (stdout, stderr string, err error)

Run sends the command with optional standard input to the currently open client SSH target as a new ssh.Session. If the SSH connection has not yet been established (c.Connected is false) [Connect] is called to establish a new client connection. Run returns an error if one is generated by the ssh/Session.Run call or if a new session could not be created (including attempting a new session on a timed-out connection or one that has been closed for any other reason.) It is the responsibility of the called to respond to such errors according to controller policy and associated method calls.

Example
// Change YAML data and remove one / from Output
// to test locally. (But never commit actual keys.)

yml := []byte(`
user:
  name: user
  key: |
    -----BEGIN OPENSSH PRIVATE KEY-----
    b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
    QyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQAAAJDswLYs7MC2
    LAAAAAtzc2gtZWQyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQ
    AAAEDWFaCmeeFjBMAzJvtf6z24ai1dHf2FSUmuHrONv/5K6XT9d1zfQk0nH4fVu+z2hns8
    4gGL9jJnhQF9D+gTJ985AAAACXJ3eHJvYkB0dgECAwQ=
    -----END OPENSSH PRIVATE KEY-----
host:
    addr: localhost
    auth: |
      |1|r3meMBTG9TZiPoVHg1n+o1N1xJk=|9I891Skl7BcqG/vaT6wXxt6bZUk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDqzw7+sN4aVOQqgTA5tC9pN/+M0KOcib3lRAGQ+MSKk/4MbdJY2REavrwRetreaIZTkZx4ykTAJ3CeCK45IzsY=
`)

client := new(ssh.Client)
err := yaml.Unmarshal(yml, client)
if err != nil {
	fmt.Println(err)
}

// basic command (no stdin)
stdout, stderr, err := client.Run(`printf hello`, ``)
fmt.Println(`-----`)
fmt.Println(stdout)
fmt.Println(stderr)

// with stdin
stdout, stderr, _ = client.Run(`cat`, `i'm a cat`)
fmt.Println(`-----`)
fmt.Println(stdout)
fmt.Println(stderr)

// with stderr
stdout, stderr, _ = client.Run(`ls notafile`, ``)
fmt.Println(`-----`)
fmt.Println(stdout)
fmt.Println(stderr)
Output:

-----
hello

-----
i'm a cat

-----

ls: cannot access 'notafile': No such file or directory

func (*Client) SSHClient added in v0.3.0

func (c *Client) SSHClient() *ssh.Client

SSHClient returns a pointer to the internal ssh.Client used for all connections and sessions. Only set after first call to Connect.

type Controller added in v0.3.0

type Controller struct {
	Clients []*Client
}

Controller is responsible for coordinating work requests destined for the target ssh servers as contained in its list of Clients. A Controller zero value (one with nil clients list) is safe to use for all methods but [Init] can be called to add clients as a convenience.

ctl := new(ssh.Controller).Init(cl1,cl2)
Example (From_YAMLJSON)
// YAML references are supported.

yml := []byte(`
users:
  user: &user
    name: user
    key: |
      -----BEGIN OPENSSH PRIVATE KEY-----
      b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
      QyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8gAAAJAZeyGhGXsh
      oQAAAAtzc2gtZWQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8g
      AAAEDdV9IJ3LNTiK7D0MFz7IR1Cz/VdqqH6SgOtiDz8/5073SN2HaGCU8lyGytWCcmNNv1
      tUciO6BLnCUmKmcgmI7yAAAACXJ3eHJvYkB0dgECAwQ=
      -----END OPENSSH PRIVATE KEY-----

hosts:
  localhost: &localhost
    addr: localhost
    auth: |
      randomoption ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/WBBaaNFajVHCL0+rQqWP3zhpyXo357iPUvl0GGHWrY6t42WTNJ+bk8shRq7eq8KwefZeL4YvsnekcZb8Uq+8=

clients:
  - host: *localhost
    user: *user
    port: 2221
    timeout: 5m
  - host: *localhost
    user: *user
    port: 2222
    timeout: 5m
  - host: *localhost
    user: *user
    port: 2223
    timeout: 5m
`)

ctl := new(ssh.Controller)
err := yaml.Unmarshal(yml, ctl)
if err != nil {
	fmt.Println(err)
}
fmt.Println(ctl.Clients[0].User.Name)
fmt.Println(ctl.Clients[1].User.Name)
fmt.Println(ctl.Clients[2].User.Name)
fmt.Println(ctl.Clients[0].Port)
fmt.Println(ctl.Clients[1].Port)
fmt.Println(ctl.Clients[2].Port)
fmt.Println(ctl.Clients[0].Timeout)
Output:

user
user
user
2221
2222
2223
5m0s

func (*Controller) Connect added in v0.3.0

func (c *Controller) Connect() *Controller

Connect synchronously calls Connect on all Clients in order ensuring that all have successfully connected before returning. No attempt at error checking for successful connections is attempted but the Client.Connected and Client.LastError can be checked when needed. A reference to self is returned as convenience.

func (*Controller) Init added in v0.3.0

func (c *Controller) Init(clients ...*Client) *Controller

Init returns a pointer to a Controller with the Clients list initialized. Init can be called later to set the internal Client list to a newly created one. When called on a Controller with an existing Clients list replaces it with a new list. If no clients are passed, simply initializes the internal Clients list to an empty list.

func (*Controller) LogStatus added in v0.3.0

func (c *Controller) LogStatus()

func (*Controller) RandomClient added in v0.3.0

func (c *Controller) RandomClient() *Client

RandomClient returns a random active client from the Clients list skipping any that are not connected. Returns nil if no connected clients are available.

Example (None)
package main

import (
	"fmt"

	"github.com/rwxrob/ssh"
)

func main() {

	// idle, unconnected
	c1 := new(ssh.Client)
	c2 := new(ssh.Client)
	c3 := new(ssh.Client)

	ctl := new(ssh.Controller).Init(c1, c2, c3)
	client := ctl.RandomClient()
	fmt.Println(client)

}
Output:

<nil>
Example (Single_Good)
// YAML references are supported.

yml := []byte(`
users:
  user: &auser
    name: user
    key: |
      -----BEGIN OPENSSH PRIVATE KEY-----
      b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
      QyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQAAAJDswLYs7MC2
      LAAAAAtzc2gtZWQyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQ
      AAAEDWFaCmeeFjBMAzJvtf6z24ai1dHf2FSUmuHrONv/5K6XT9d1zfQk0nH4fVu+z2hns8
      4gGL9jJnhQF9D+gTJ985AAAACXJ3eHJvYkB0dgECAwQ=
      -----END OPENSSH PRIVATE KEY-----
hosts:
  localhost: &localhost
    addr: localhost
    auth: |
      |1|r3meMBTG9TZiPoVHg1n+o1N1xJk=|9I891Skl7BcqG/vaT6wXxt6bZUk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDqzw7+sN4aVOQqgTA5tC9pN/+M0KOcib3lRAGQ+MSKk/4MbdJY2REavrwRetreaIZTkZx4ykTAJ3CeCK45IzsY=

clients:
  - host: *localhost
    user: *auser
    comment: number one
  - host: *localhost
    user: *auser
    comment: number two
  - host: *localhost
    user: *auser
    comment: number three
`)

ctl := new(ssh.Controller)
err := yaml.Unmarshal(yml, ctl)
if err != nil {
	fmt.Println(err)
}

ctl.Connect()
client := ctl.RandomClient()
if client != nil {
	fmt.Println(client.Comment)
}
Output:

func (*Controller) RunOnAny added in v0.3.0

func (c *Controller) RunOnAny(cmd string, stdin []byte) (stdout, stderr string, err error)

RunOnAny calls Client.Run on a random client from the [Clients] list. If error returned is of type net.OpError the Client.Connected is set to false and the next client in the [Clients] order is attempted. Then client producing the error has Client.Connect called in a separate goroutine (which, if successful, restores its Client.Connected status to true). If none of the clients are connected then an AllUnavailable error is returned.

Example
yml := []byte(`
pem: &ukey |
  -----BEGIN OPENSSH PRIVATE KEY-----
  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
  QyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQAAAJDswLYs7MC2
  LAAAAAtzc2gtZWQyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQ
  AAAEDWFaCmeeFjBMAzJvtf6z24ai1dHf2FSUmuHrONv/5K6XT9d1zfQk0nH4fVu+z2hns8
  4gGL9jJnhQF9D+gTJ985AAAACXJ3eHJvYkB0dgECAwQ=
  -----END OPENSSH PRIVATE KEY-----

user1: &user1
  name: user1
  key: *ukey
user2: &user2
  name: user2
  key: *ukey
user3: &user3
  name: user3
  key: *ukey

hosts:
  localhost: &localhost
    addr: localhost
    auth: |
      |1|r3meMBTG9TZiPoVHg1n+o1N1xJk=|9I891Skl7BcqG/vaT6wXxt6bZUk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDqzw7+sN4aVOQqgTA5tC9pN/+M0KOcib3lRAGQ+MSKk/4MbdJY2REavrwRetreaIZTkZx4ykTAJ3CeCK45IzsY=

clients:
  - host: *localhost
    user: *user1
  - host: *localhost
    user: *user2
  - host: *localhost
    user: *user3
`)

ctl := new(ssh.Controller)
err := yaml.Unmarshal(yml, ctl)
if err != nil {
	fmt.Println(err)
}

ctl.Connect()
if err != nil {
	fmt.Println(err)
}

stdin, _, _ := ctl.RunOnAny(`whoami`, ``)
fmt.Println(stdin)
Output:

Example (No_Clients)
package main

import (
	"fmt"

	"github.com/rwxrob/ssh"
)

func main() {

	ctl := new(ssh.Controller)
	_, _, err := ctl.RunOnAny(`echo hello`, ``)
	fmt.Println(err)

}
Output:

all SSH client targets are unavailable
Example (Single_Good)
yml := []byte(`
pem: &ukey |
  -----BEGIN OPENSSH PRIVATE KEY-----
  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
  QyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQAAAJDswLYs7MC2
  LAAAAAtzc2gtZWQyNTUxOQAAACB0/Xdc30JNJx+H1bvs9oZ7POIBi/YyZ4UBfQ/oEyffOQ
  AAAEDWFaCmeeFjBMAzJvtf6z24ai1dHf2FSUmuHrONv/5K6XT9d1zfQk0nH4fVu+z2hns8
  4gGL9jJnhQF9D+gTJ985AAAACXJ3eHJvYkB0dgECAwQ=
  -----END OPENSSH PRIVATE KEY-----

user1: &user1
  name: user1
  key: *ukey
user2: &user2
  name: user2
  key: *ukey
user3: &user3
  name: user3
  key: *ukey

hosts:
  localhost: &localhost
    addr: localhost
    auth: |
      |1|r3meMBTG9TZiPoVHg1n+o1N1xJk=|9I891Skl7BcqG/vaT6wXxt6bZUk= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDqzw7+sN4aVOQqgTA5tC9pN/+M0KOcib3lRAGQ+MSKk/4MbdJY2REavrwRetreaIZTkZx4ykTAJ3CeCK45IzsY=

clients:
  - host: *localhost
    user: *user1
  - host: *localhost
    user: *user2
  - host: *localhost
    user: *user3
`)

ctl := new(ssh.Controller)
err := yaml.Unmarshal(yml, ctl)
if err != nil {
	fmt.Println(err)
}

// only connect one of the clients, others are bogus
err = ctl.Clients[1].Connect()
if err != nil {
	fmt.Println(err)
}

stdin, _, _ := ctl.RunOnAny(`whoami`, ``)
fmt.Println(stdin)
Output:

user2

type Host added in v0.2.0

type Host struct {

	// Network host name or IP address (required).
	Addr string

	// Complete line taken in the authorized_hosts format (optional). When
	// included triggers returning ssh.FixedHostKey(pubkey) for
	// KeyCallback.
	Auth string
}

Host represents a single host on the network that is hosting a secure shell server. A Host may be safely marshaled/unmarshaled to/from JSON/YAML.

Example (From_YAMLJSON)
var err error

yml := []byte(`
addr: host1
auth: "randomoption ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/WBBaaNFajVHCL0+rQqWP3zhpyXo357iPUvl0GGHWrY6t42WTNJ+bk8shRq7eq8KwefZeL4YvsnekcZb8Uq+8="
`)

// YAML
host1 := new(ssh.Host)
err = yaml.Unmarshal(yml, host1)
if err != nil {
	fmt.Println(err)
}
fmt.Println(host1.Addr)
fmt.Println(host1.Auth)

jsn := []byte(`{"addr":"host2","auth":"randomoption ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/WBBaaNFajVHCL0+rQqWP3zhpyXo357iPUvl0GGHWrY6t42WTNJ+bk8shRq7eq8KwefZeL4YvsnekcZb8Uq+8="}`)

// JSON (which *is* YAML)
host2 := new(ssh.Host)
err = yaml.Unmarshal(jsn, host2)
if err != nil {
	fmt.Println(err)
}
fmt.Println(host2.Addr)
fmt.Println(host2.Auth)
Output:

host1
randomoption ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/WBBaaNFajVHCL0+rQqWP3zhpyXo357iPUvl0GGHWrY6t42WTNJ+bk8shRq7eq8KwefZeL4YvsnekcZb8Uq+8=
host2
randomoption ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBI/WBBaaNFajVHCL0+rQqWP3zhpyXo357iPUvl0GGHWrY6t42WTNJ+bk8shRq7eq8KwefZeL4YvsnekcZb8Uq+8=

func (Host) KeyCallback added in v0.3.0

func (h Host) KeyCallback() (ssh.HostKeyCallback, error)

KeyCallback returns ssh.FixedHostKey(pubkey) where pubkey is derived from the Auth string if Auth is not nil. Otherwise, returns ssh.InsecureIgnoreHostKey().

type User added in v0.2.0

type User struct {

	// Name of user on target system hosting SSH server.
	Name string

	// Private key in PEM format.
	Key string
}

User represents a single SSH user on the target host authenticated by a private key. A User may be safely marshaled/unmarshaled from JSON/YAML.

Example (From_YAMLJSON)
var err error

yml := []byte(`
name: user1
key: |-
  -----BEGIN OPENSSH PRIVATE KEY-----
  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
  QyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8gAAAJAZeyGhGXsh
  oQAAAAtzc2gtZWQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8g
  AAAEDdV9IJ3LNTiK7D0MFz7IR1Cz/VdqqH6SgOtiDz8/5073SN2HaGCU8lyGytWCcmNNv1
  tUciO6BLnCUmKmcgmI7yAAAACXJ3eHJvYkB0dgECAwQ=
  -----END OPENSSH PRIVATE KEY-----
`)

// YAML
user1 := new(ssh.User)
err = yaml.Unmarshal(yml, user1)
if err != nil {
	fmt.Println(err)
}
fmt.Println(user1.Name)
fmt.Println(user1.Key)

jsn := []byte(`{"name": "user2","key":"-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\nQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8gAAAJAZeyGhGXsh\noQAAAAtzc2gtZWQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8g\nAAAEDdV9IJ3LNTiK7D0MFz7IR1Cz/VdqqH6SgOtiDz8/5073SN2HaGCU8lyGytWCcmNNv1\ntUciO6BLnCUmKmcgmI7yAAAACXJ3eHJvYkB0dgECAwQ=\n-----END OPENSSH PRIVATE KEY-----"}`)

// JSON (which *is* YAML)
user2 := new(ssh.User)
err = yaml.Unmarshal(jsn, user2)
if err != nil {
	fmt.Println(err)
}
fmt.Println(user2.Name)
fmt.Println(user1.Key)
Output:

user1
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8gAAAJAZeyGhGXsh
oQAAAAtzc2gtZWQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8g
AAAEDdV9IJ3LNTiK7D0MFz7IR1Cz/VdqqH6SgOtiDz8/5073SN2HaGCU8lyGytWCcmNNv1
tUciO6BLnCUmKmcgmI7yAAAACXJ3eHJvYkB0dgECAwQ=
-----END OPENSSH PRIVATE KEY-----
user2
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8gAAAJAZeyGhGXsh
oQAAAAtzc2gtZWQyNTUxOQAAACB0jdh2hglPJchsrVgnJjTb9bVHIjugS5wlJipnIJiO8g
AAAEDdV9IJ3LNTiK7D0MFz7IR1Cz/VdqqH6SgOtiDz8/5073SN2HaGCU8lyGytWCcmNNv1
tUciO6BLnCUmKmcgmI7yAAAACXJ3eHJvYkB0dgECAwQ=
-----END OPENSSH PRIVATE KEY-----

func (User) Signer added in v0.2.0

func (u User) Signer() (ssh.Signer, error)

Signer trims and parses then content of Key and returns a new crypto/ssh.Signer.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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