SSH ProxyCommand Workflow

The most common pattern for using Boundary to SSH is with the built-in boundary connect ssh command. However, there are more idiomatic approaches that can be employed to make Boundary transparent to users, and at the same time simplify common developer and operator workflows. Using ProxyCommand to execute a proxy when invoking the SSH client is a common practice. In this workflow, we’ll cover configuring your SSH client to execute the boundary command, enabling a simplified SSH workflow that leverages Boundary’s authenticated proxy for accessing targets.

SSH Configuration

Start by configuring a Host entry in .ssh/ssh_config for localhost:

  1. Host ttcp_*
  2. ProxyCommand sh -c "boundary connect -target-id %n -exec nc -- {{boundary.ip}} {{boundary.port}}"
  1. Host ttcp_* ProxyCommand sh -c "boundary connect -target-id %n -exec nc -- {{boundary.ip}} {{boundary.port}}"

The ProxyCommand tells the SSH client to invoke boundary connect. We are passing the -exec nc flag to boundary connect to wrap netcat, and then pass the boundary.ip and boundary.port variables as arguments to nc. This allows us to proxy our SSH connection through a local netcat tunnel that’s managed as a Boundary session.

When you run ssh ttcp_1234567890 (example target ID), SSH will invoke boundary connect, and will tunnel the traffic through the local Boundary proxy in a wrapped netcat session, passing in the target ID as the hostname to the boundary command.

Authenticate to Boundary

Before ssh can invoke boundary connect you need to authenticate. In this example, we’re using boundary dev with the default username (admin) and password (password):

  1. $ boundary authenticate password -login-name=admin -password password -auth-method-id=ampw_1234567890
  1. $ boundary authenticate password -login-name=admin -password password -auth-method-id=ampw_1234567890

SSH to Target

Running Boundary in dev mode creates other default resources such as a target and auth method. The default target (ttcp_1234567890) is already setup with a host at localhost and a default port of 22.

You can simply run ssh as you normally would and all the traffic will be proxied over the Boundary proxy to your target machine:

  1. $ ssh ttcp_1234567890
  2. Password:
  3. Last login: Thu Nov 19 10:32:09 2020
  4. ~
  1. $ ssh ttcp_1234567890Password:Last login: Thu Nov 19 10:32:09 2020 ~

On the controller, you should see the session being invoked in the logs:

  1. controller.worker-handler: connection established: session_id=s_WkaQbqmrJx connection_id=sc_RDYNFFI2M4 client_tcp_address=127.0.0.1 client_tcp_port=57421 endpoint_tcp_address=::1 endpoint_tcp_port=22
  1. controller.worker-handler: connection established: session_id=s_WkaQbqmrJx connection_id=sc_RDYNFFI2M4 client_tcp_address=127.0.0.1 client_tcp_port=57421 endpoint_tcp_address=::1 endpoint_tcp_port=22

You can inspect the session on the CLI as well:

  1. $ boundary sessions read -id $(boundary sessions list -scope-id p_1234567890 -format json | jq '.[0]["id"]' | tr -d '"')
  2. Session information:
  3. Auth Token ID: at_a8itRfDSyV
  4. Created Time: Thu, 19 Nov 2020 10:52:44 PST
  5. Endpoint: tcp://localhost:22
  6. Expiration Time: Thu, 19 Nov 2020 18:52:44 PST
  7. Host ID: hst_1234567890
  8. Host Set ID: hsst_1234567890
  9. ID: s_WkaQbqmrJx
  10. Status: active
  11. Target ID: ttcp_1234567890
  12. Type: tcp
  13. Updated Time: Thu, 19 Nov 2020 10:52:47 PST
  14. User ID: u_1234567890
  15. Version: 2
  16. Scope:
  17. ID: p_1234567890
  18. Name: Generated project scope
  19. Parent Scope ID: o_1234567890
  20. Type: project
  21. States:
  22. Start Time: Thu, 19 Nov 2020 10:52:47 PST
  23. Status: active
  24. End Time: Thu, 19 Nov 2020 10:52:47 PST
  25. Start Time: Thu, 19 Nov 2020 10:52:44 PST
  26. Status: pending
  1. $ boundary sessions read -id $(boundary sessions list -scope-id p_1234567890 -format json | jq '.[0]["id"]' | tr -d '"')
  2. Session information: Auth Token ID: at_a8itRfDSyV Created Time: Thu, 19 Nov 2020 10:52:44 PST Endpoint: tcp://localhost:22 Expiration Time: Thu, 19 Nov 2020 18:52:44 PST Host ID: hst_1234567890 Host Set ID: hsst_1234567890 ID: s_WkaQbqmrJx Status: active Target ID: ttcp_1234567890 Type: tcp Updated Time: Thu, 19 Nov 2020 10:52:47 PST User ID: u_1234567890 Version: 2
  3. Scope: ID: p_1234567890 Name: Generated project scope Parent Scope ID: o_1234567890 Type: project
  4. States: Start Time: Thu, 19 Nov 2020 10:52:47 PST Status: active
  5. End Time: Thu, 19 Nov 2020 10:52:47 PST Start Time: Thu, 19 Nov 2020 10:52:44 PST Status: pending

Advanced Usage

Another way to access targets using ProxyCommand is by setting the Host value to the target domain instead of the target ID. This is useful if you want to use the domain name instead of the target ID when forking SSH. An extra step is required to make this work where you must authorize the session to the target and store it in your environment since we’re no longer passing the target ID to the SSH command. This will allow Boundary to pull the target information from the token you created in the authorize session command.

Configure SSH

  1. Host localhost
  2. # requires BOUNDARY_CONNECT_AUTHZ_TOKEN to be set from authorize-session command
  3. ProxyCommand sh -c "boundary connect -exec nc -- {{boundary.ip}} {{boundary.port}}"
  1. Host localhost # requires BOUNDARY_CONNECT_AUTHZ_TOKEN to be set from authorize-session command ProxyCommand sh -c "boundary connect -exec nc -- {{boundary.ip}} {{boundary.port}}"

Authenticate to Boundary

Before ssh can invoke boundary connect you need to authenticate. In this example, we’re using boundary dev with the default username (admin) and password (password):

  1. $ boundary authenticate password -login-name=admin -password password -auth-method-id=ampw_1234567890
  1. $ boundary authenticate password -login-name=admin -password password -auth-method-id=ampw_1234567890

Authorize the Session

Running Boundary in dev mode creates other default resources such as a target and auth method. The default target (ttcp_1234567890) is already setup with a host at localhost and a default port of 22.

Authorize a session to the default target and store the token in the BOUNDARY_CONNECT_AUTHZ_TOKEN environment variable. This will be used by Boundary when it’s invoked by SSH:

  1. $ export BOUNDARY_CONNECT_AUTHZ_TOKEN=$(boundary targets authorize-session -id ttcp_1234567890 -format json | jq '.authorization_token' | tr -d '"')
  1. $ export BOUNDARY_CONNECT_AUTHZ_TOKEN=$(boundary targets authorize-session -id ttcp_1234567890 -format json | jq '.authorization_token' | tr -d '"')

SSH to Target

  1. $ ssh user@localhost
  2. Password:
  3. Last login: Thu Nov 19 10:32:09 2020
  4. ~
  1. $ ssh user@localhostPassword:Last login: Thu Nov 19 10:32:09 2020 ~