guardianagent

package module
v0.7.2-beta Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2017 License: BSD-3-Clause Imports: 32 Imported by: 9

README

Guardian Agent: secure ssh-agent forwarding for Mosh and SSH

Traditional ssh-agent forwarding can be dangerous: the local ssh-agent has to sign opaque challenges with the user's private key, without knowing (a) what intermediary host is asking for the signature, (b) what remote server that intermediary host wants to authenticate to, or (c) what command the intermediary host wants to execute on the remote server.

A compromised intermediary can send rogue challenges and use the user's identity to authenticate to other servers or to run unauthorized commands. So you might enable ssh-agent forwarding and be asked yes or no on signing "something," and you think it's allowing an EC2 machine to run "git push" to GitHub. But actually it's allowing a different EC2 machine (that you also are logged in to) to connect to some other sensitive server that you have permissions on and add an evil key to your authorized_keys file.)

Compromised

Guardian Agent provides secure ssh-agent forwarding. A user first runs sga-guard on her local machine (on which she stores her private SSH keys) to securely forward her ssh-agent to an intermediary machine (e.g., on AWS). She can then use sga-ssh on the intermediary machine as a drop-in replacement to ssh. The local sga-guard verifies the identity of (a) the intermediary host, (b) the remote server, and (c) the command*, either by prompting the user or based on a stored security policy. After all the details are verified, the connection is handed off to the intermediary (so the bulk of the data is not proxied through the local host).

Example


WARNING!

This tool is in beta and we're working to improve it. Feedback is greatly appreciated, but please use at your own risk.


Installation

Using Guardian Agent requires installation both on your local machine (the one with your SSH private keys) and on each of the intermediary machines you want to securely forward ssh-agent to (the machines on which you want to run an SSH client without having the keys on them). No installation is required on the server side.

Ubuntu installation

sudo apt-get install openssh-client autossh ssh-askpass
curl -L https://api.github.com/repos/StanfordSNR/guardian-agent/releases/latest | grep browser_download_url | grep 'linux' | cut -d'"' -f 4 | xargs curl -Ls | tar xzv
sudo cp sga_linux_amd64/* /usr/local/bin

macOS installation

brew install autossh ssh-askpass
curl -L https://api.github.com/repos/StanfordSNR/guardian-agent/releases/latest | grep browser_download_url | grep 'darwin' | cut -d'"' -f 4 | xargs curl -L | tar xzv
sudo cp sga_darwin_amd64/* /usr/local/bin

Other

  1. Install the following dependencies: OpenSSH client, autossh, ssh-askpass.
  2. Obtain the latest release for your platform. Alternatively, you may opt to build from source.
  3. Extract the executables (sga-guard, sga-guard-bin, sga-ssh, and sga-stub) from the tarball to a directory in the user's PATH.

Basic Usage

Make sure SSH guardian agent is installed on both your local and intermediary machine.

On your local machine

Start guarded SSH agent forwarding to the intermediary machine:

[local]$ sga-guard <intermediary>

You should then expect to see the following message:

[local]$ sga-guard aws-ubu
Connecting to aws-ubu to set up forwarding...
Forwarding to aws-ubu setup successfully. Waiting for incoming requests...

Guarded agent forwarding is now enabled on the intermediary.

On the intermediary

Connect to the intermediary (e.g., using standard ssh or mosh). Install guardian-agent. To enable several common tools (scp, git, rsync, mosh) to use the guardian agent instead of the default ssh program:

[intermediary]$ source sga-env.sh

You can also add this line to your ~/.bashrc/~.zshrc/... file on the intermediary hosts.

You can then use git, scp, rsync, mosh as you would normally do.

[intermediary]$ git clone git@github.com:user/repo
...
[intermediary]$ scp foo.txt remote-host:Documents/foo.txt
...

You can also use sga-ssh as a drop-in replacement to an ssh client:

[intermediary]$ sga-ssh <server> [command]

Advanced Usage

Command verification

Command verification requires the server to support the no-more-sessions extension. This is extension is present on OpenSSH servers, but unfortunately not implemented on other SSH servers (including GitHub, which uses libssh). When executing a command on a server that does not support this extension, only the identity of the intermediary and the identity of the server can be constrained and verified by the agent (but not the contents of the command).

Prompt types

Guardian Agent supports two types of interactive prompts: graphical and terminal-based. The graphical prompt requires the DISPLAY environment variable to be set to the appropriate X11 server.
If running in a terminal-only session (in which the DISPLAY environment variable is not set), a textual prompt will be used instead.

Customizing the SSH command

When using sga-guard, the default SSH client on the local machine is used to set up the connection. This requires ssh to be found in the user's PATH. To specify an alternative SSH client or specifying additional argument to the client, use the --ssh command-line flag.

Stub location

If the sga-stub is not installed in the user's PATH on the intermediary machine, its location must be specified when setting up secure agent forwarding from the local machine:

[local]$ sga-guard --stub=<PATH-TO-STUB> <intermediary>

Building from Source

  1. Install go 1.8+
  2. Get and build the sources:
go get github.com/StanfordSNR/guardian-agent/...
  1. Copy the built binaries (sga-guard-bin, sga-ssh, and sga-stub) from $GOPATH/bin to a directory in the user's PATH.
  2. Copy the scripts $GOPATH/src/github.com/StanfordSNR/guardian-agent/scripts/sga-guard and $GOPATH/src/github.com/StanfordSNR/guardian-agent/scripts/sga-env.sh to a directory in the user's PATH.

Troubleshooting

In case of unexpected behavior, please consider opening an issue in our issue tracker. We'd also greatly appreciate if you could run the tool in debug mode by setting the --debug and --logfile=<LOG-FILE> flags and attach the log file to the issue.

FAQ

Q: Is ssh-agent forwarding really insecure? What is the point of Guardian Agent?

A: The ssh(1) man page warns that "Agent forwarding should be enabled with caution," because the ssh-agent cannot verify (a) which intermediary machine is making the request, (b) which remote server the intermediary wants to authenticate to, or (c) what command the intermediary plans to run on the remote server. The agent simply signs a blank check--- an opaque challenge from an unknown server that will allow the intermediary to execute any sequence of commands on the user's behalf. Several commentators have noted that this creates risks that may not be widely appreciated.

Guardian Agent is a prototype of a system for secure agent forwarding that could be enabled on every outgoing connection, because the local agent can verify and enforce security policies regarding who wants to do what to whom.

Q: What if I only use ssh-agent forwarding when I SSH to intermediaries that I trust?

A: If the user trusts the software (and system administrator) on the intermediary host, it is essentially fine to use ssh-agent forwarding as it exists today. However, with this level of trust, it may also be fine to simply place a private key on the intermediary's hard drive and use that to authenticate to remote servers, rather than forwarding agent requests back to the local agent.

Q: Can I use this to constrain an intermediary to only pull from (or only push to) a limited set of remote Git repositories?

A: Yes, if the remote Git server is running an SSH server (such as OpenSSH) that supports no-more-sessions and allows Guardian Agent to limit the command. (The name of the repository, and the difference between pulling and pushing, are both represented in the command.) Among popular Git-hosting services that we are aware of, currently only GitLab appears to support this currently. GitHub and Bitbucket use other SSH implementations and do not allow Guardian Agent to constrain the intermediary to only push or pull from certain repositories.

Q: Is Guardian Agent secure?

A: Guardian Agent is a technology preview that was first released for beta testing in October 2017. It has not accumulated enough testing and scrutiny to make claims that the implementation is bulletproof.

Q: Why did you write Guardian Agent in Go?

A: Guardian Agent is a technology preview intended to solicit feedback from the community, especially with regards to the basic design of a secure ssh-agent forwarding mechanism that works with unmodified remote SSH servers. We found Go and the Go SSH library to be helpful in rapidly prototyping this tool.

Q: What is the connection to Mosh (mobile shell)?

A: Many Mosh users have asked for ssh-agent forwarding support. Guardian Agent was developed by some of the Mosh developers and can be used with Mosh today. Based on feedback to this prototype, we may integrate Guardian Agent more fully into Mosh as a system for secure ssh-agent forwarding that is safe enough to leave on by default.

Q: Who wrote Guardian Agent?

A: Guardian Agent was developed by students and faculty in the Stanford University Department of Computer Science (Dima Kogan and Henri Stern, advised by Keith Winstein and David Mazières).

Q: Where should I send feedback?

A: Please file an issue on GitHub.

Development

Detailed Design

Documentation

Index

Constants

View Source
const (
	Terminal = iota
	Display
)
View Source
const AgentGuardExtensionType = "agent-guard@cs.stanford.edu"
View Source
const AgentGuardSockName = ".agent-guard-sock"
View Source
const MaxAgentPacketSize = 10 * 1024
View Source
const MsgAgentCExtension = 27
View Source
const MsgAgentFailure = 5
View Source
const MsgAgentForwardingNotice = 206
View Source
const MsgAgentSuccess = 6
View Source
const MsgExecutionApproved = 3
View Source
const MsgExecutionDenied = 2
View Source
const MsgExecutionRequest = 1
View Source
const MsgHandoffComplete = 10
View Source
const MsgHandoffFailed = 11

Variables

View Source
var Version string

Functions

func CreateSocket

func CreateSocket(name string) (s net.Listener, finalName string, err error)

func HostKeyCallback

func HostKeyCallback(hostname string, remote net.Addr, key ssh.PublicKey, ui UI) error

func ReadControlPacket

func ReadControlPacket(r io.Reader) (msgNum byte, payload []byte, err error)

func ReplaceSSHAuthSockEnv

func ReplaceSSHAuthSockEnv(env []string, newVal string) (newEnv []string, err error)

func RunSSHCommand

func RunSSHCommand(cmd SSHCommand) error

Run starts a delegated session.

func UserRuntimeDir

func UserRuntimeDir() string

func UserTempDir

func UserTempDir() string

func WriteControlPacket

func WriteControlPacket(w io.Writer, msgNum byte, payload []byte) error

Types

type Agent

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

func NewGuardian

func NewGuardian(policyConfigPath string, inType InputType) (*Agent, error)

func (*Agent) HandleConnection

func (agent *Agent) HandleConnection(conn net.Conn) error

type AgentCExtensionMsg

type AgentCExtensionMsg struct {
	ExtensionType string
	Contents      []byte
}

type AgentFailureMsg

type AgentFailureMsg struct{}

type AgentForwardingNoticeMsg

type AgentForwardingNoticeMsg struct {
	Client string
}

type AllowedCommands

type AllowedCommands struct {
	AllCommands bool     `json:"AllCommands"`
	Commands    []string `json:"Commands"`
}

type AskPassUI

type AskPassUI struct{}

func (AskPassUI) Alert

func (AskPassUI) Alert(msg string)

func (AskPassUI) Ask

func (AskPassUI) Ask(params Prompt) (reply int, err error)

func (AskPassUI) AskPassword

func (AskPassUI) AskPassword(msg string) (string, error)

func (AskPassUI) Confirm

func (apui AskPassUI) Confirm(msg string) bool

func (AskPassUI) Inform

func (AskPassUI) Inform(msg string)

type CloseWriter

type CloseWriter interface {
	CloseWrite() error
}

type CommonOptions

type CommonOptions struct {
	Debug bool `long:"debug" description:"Show debug information"`

	Username string `short:"l" description:"Specifies the user to log in as on the remote machine"`

	LogFile string `long:"log" description:"log file"`

	Version bool `long:"version" short:"V" description:"Display the version number and exit"`
}

type CustomConn

type CustomConn struct {
	net.Conn
	RemoteAddress net.Addr
	// contains filtered or unexported fields
}

func (*CustomConn) BytesRead

func (cc *CustomConn) BytesRead() int

func (*CustomConn) BytesWritten

func (cc *CustomConn) BytesWritten() int

func (*CustomConn) Read

func (cc *CustomConn) Read(p []byte) (n int, err error)

func (*CustomConn) RemoteAddr

func (cc *CustomConn) RemoteAddr() net.Addr

func (*CustomConn) Write

func (cc *CustomConn) Write(b []byte) (n int, err error)

type ExecutionApprovedMessage

type ExecutionApprovedMessage struct {
}

type ExecutionDeniedMessage

type ExecutionDeniedMessage struct {
	Reason string
}

type ExecutionRequestMessage

type ExecutionRequestMessage struct {
	User    string
	Command string
	Server  string
}

type FancyTerminalUI

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

func (*FancyTerminalUI) Alert

func (tui *FancyTerminalUI) Alert(msg string)

func (*FancyTerminalUI) Ask

func (tui *FancyTerminalUI) Ask(params Prompt) (reply int, err error)

func (*FancyTerminalUI) AskPassword

func (tui *FancyTerminalUI) AskPassword(msg string) (string, error)

func (*FancyTerminalUI) Confirm

func (tui *FancyTerminalUI) Confirm(msg string) bool

func (*FancyTerminalUI) Inform

func (tui *FancyTerminalUI) Inform(msg string)

type HandoffCompleteMessage

type HandoffCompleteMessage struct {
	NextTransportByte uint32
}

type HandoffFailedMessage

type HandoffFailedMessage struct {
	Msg string
}

type InputType

type InputType uint8

type Policy

type Policy struct {
	Store *Store
	UI    UI
}

func (*Policy) RequestApproval

func (policy *Policy) RequestApproval(scope Scope, cmd string) error

func (*Policy) RequestApprovalForAllCommands

func (policy *Policy) RequestApprovalForAllCommands(scope Scope) error

type Prompt

type Prompt struct {
	Question string
	Choices  []string
}

type SSHCommand

type SSHCommand struct {
	HostPort     string
	Username     string
	Cmd          string
	ProxyCommand string
	StdinNull    bool
	ForceTty     bool
}

type SSHFwd

type SSHFwd struct {
	SSHProgram         string
	SSHArgs            []string
	Host               string
	RemoteReadableName string
	RemoteStubName     string
	// contains filtered or unexported fields
}

func (*SSHFwd) Accept

func (fwd *SSHFwd) Accept() (net.Conn, error)

func (*SSHFwd) Close

func (fwd *SSHFwd) Close()

func (*SSHFwd) RunLocal

func (fwd *SSHFwd) RunLocal(cmd string) error

func (*SSHFwd) RunRemote

func (fwd *SSHFwd) RunRemote(cmd string) error

func (*SSHFwd) SetupForwarding

func (fwd *SSHFwd) SetupForwarding() error

type Scope

type Scope struct {
	Client          string `json:"Client"`
	ServiceUsername string `json:"ServiceUsername"`
	ServiceHostname string `json:"ServiceHostname"`
}

type Store

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

func NewStore

func NewStore(configPath string) (store *Store, err error)

func (*Store) AllowAll

func (store *Store) AllowAll(scope Scope) (err error)

func (*Store) AllowCommand

func (store *Store) AllowCommand(scope Scope, cmd string) (err error)

func (*Store) AreAllAllowed

func (store *Store) AreAllAllowed(scope Scope) bool

func (*Store) IsAllowed

func (store *Store) IsAllowed(scope Scope, cmd string) bool

func (*Store) MarshalJSON

func (store *Store) MarshalJSON() ([]byte, error)

func (*Store) Save

func (store *Store) Save() (err error)

func (*Store) UnmarshalJSON

func (store *Store) UnmarshalJSON(b []byte) error

type UI

type UI interface {
	Ask(prompt Prompt) (int, error)
	Confirm(msg string) bool
	Inform(msg string)
	Alert(msg string)
	AskPassword(msg string) (string, error)
}

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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