sshpiper

repository·master·Indexed 23 days ago

https://github.com/tg123/sshpiper

A reverse proxy for SSH that provides routing, authentication mapping, and additional challenges like 2FA between downstream clients and upstream servers. It features a gRPC-based plugin system and includes a webadmin dashboard for managing live sessions, a CLI tool called sshpiperd-admin, and various plugins such as username-router, revtunnel, and failtoban.

Tokens
28K
Snippets
62
Records
164
Agent score
79%

What's inside sshpiper

  1. Understand revtunnel tunnel lifetime and behavior

    master

    The revtunnel plugin has specific lifecycle and security behaviors:

    • Tunnel Lifetime: A tunnel exists only as long as the registrar's SSH session is active. When the session ends, the tunnel is torn down. On clean disconnects, the GUID record is deleted. Re-registering does not revive an old GUID; every ssh -R creates a fresh GUID.
    • Idle Timeout: Records are evicted after 2 hours of inactivity (no traffic or handshake). This triggers a drop of the registrar's SSH connection.
    • Authentication:
      • Register-side: Always requires public key authentication.
      • Connect-side: Public key by default (matching the registrar's key or CONNECTOR_PUBKEY). If ALLOWPASSWORD was set, password auth is also supported.
    • Offline Errors: If a connection is attempted using a GUID that is known but no longer active, it is refused with an "offline" error.
  2. Use kubectl-exec bridge mode for pods without sshd

    master

    The Kubernetes plugin supports a kubectl-exec upstream mode, which allows you to bridge SSH connections directly to a pod's exec session. This is useful for pods that do not have an sshd server running.

    To enable this mode, add the following annotations to your Pipe resource:

    • sshpiper.com/kubectl_exec_cmd: "true" (or kubectl_exec_cmd): Enables the mode.
    • sshpiper.com/kubectl_sshd_cmd: Overrides the default command (defaults to /bin/sh).

    In this mode, spec.to.host is interpreted as:

    • pod
    • pod/container
    • namespace/pod/container

    Requirement: The sshpiperd service account must have RBAC permissions to get pods and create pods/exec sessions.

  3. Use regex for dynamic usernames

    master

    You can use regular expressions to match downstream usernames and map them to upstream usernames using capture groups.

    1. Set from.username_regex_match: true.
    2. Provide a regex pattern in from.username.
    3. Use $1, $2, etc., in to.username to inject the captured groups (following Regexp.Expand syntax).

    Example: A client connecting as password_world_regex will be mapped to world@example.com upstream.

    # yaml-language-server: $schema=https://raw.githubusercontent.com/tg123/sshpiper/master/plugin/yaml/schema.json
    version: "1.0"
    pipes:
    - from:
        - username: "^password_(.*?)_regex$"
          username_regex_match: true
      to:
        host: example.com:22
        username: "$1"
  4. Use magic placeholders and environment variables in paths

    master

    The YAML plugin supports placeholders and environment variables within file paths for authorized_keys, private_key, and known_hosts:

    • $DOWNSTREAM_USER: Supported in private_key, known_hosts.
    • $UPSTREAM_USER: Supported in authorized_keys, private_key, known_hosts.
    • Environment Variables: Supported in authorized_keys, private_key, known_hosts.
  5. Understand sshpiper terminology and architecture

    master

    sshpiper is a reverse proxy for sshd that supports all SSH-based protocols (SSH, SCP, port forwarding). It operates using three main components:

    • Downstream: The client side (e.g., an SSH client).
    • Upstream: The server side (e.g., the target SSH server).
    • Plugin: Handles routing from downstream to upstream and maps authentication methods. For example, a plugin can map a downstream password authentication to an upstream public key authentication.
    • Additional Challenge: Some plugins add extra authentication steps (like 2FA) before the upstream connection is established.
  6. Use Recursive Search mode

    master

    By enabling the --recursive-search flag, the plugin will search through all subdirectories of the user's directory to find the authorized_keys file. This allows for organized, nested directory structures for different upstream services under a single user.

    Example structure:

    ├── git
    │   ├── bitbucket
    │   │   └── sshpiper_upstream
    │   ├── github
    │   │   ├── authorized_keys
    │   │   ├── id_rsa
    │   │   └── sshpiper_upstream
    │   └── gitlab
    │       └── sshpiper_upstream
    └── linode....
  7. Authenticate downstream clients with SSH User Certificates

    master

    If your clients use provisioned SSH user certificates, you can verify trust using a CA public key instead of from.authorized_keys.

    • Use from.trusted_ca_keys to provide the CA public key.
    • The from.username must match one of the principals registered in the client's certificate.
    • Note: When using this method, password authentication is not valid, and the connection to the upstream cannot be via an SSH user certificate.
    # yaml-language-server: $schema=https://raw.githubusercontent.com/tg123/sshpiper/master/plugin/yaml/schema.json
    version: "1.0"
    pipes:
    - from:
        - username: "hello"
          trusted_ca_keys: /path/to/ca-key-user_ed25519.pub
      to:
        host: example.com:22
        username: "world"
        private_key: /path/to/id_rsa
  8. Enable password authentication for a tunnel

    master

    You can bypass the need for authorized_keys by enabling password authentication for a specific tunnel.

    To do this, set the environment variable ALLOWPASSWORD=1 during the registration command and ensure you use -o SendEnv=ALLOWPASSWORD.

    When connecting via the GUID, sshpiperd will forward the password directly to the upstream target. In this demo, the target password is pass.

    # Registration with password auth enabled
    ALLOWPASSWORD=1 ssh -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -o SendEnv=ALLOWPASSWORD \
        -R 0:127.0.0.1:2224 \
        -p 2222 user@127.0.0.1
    
    # Connecting via the GUID
    ssh -o StrictHostKeyChecking=no \
        -o UserKnownHostsFile=/dev/null \
        -p 2222 <GUID>@127.0.0.1
    # password: pass
  9. Run the Working Directory plugin for sshpiperd

    master

    The workingdir plugin allows sshpiperd to manage upstream configurations using a directory structure similar to /home. Each user has a directory under the root path where configuration files, authorized keys, and identity keys are stored. When a user connects, sshpiperd reads the files in workingdir/[username]/ to determine the upstream destination.

    sshpiperd workingdir --root /var/sshpiper
  10. Manage sessions via `sshpiperd-admin` CLI

    master

    The sshpiperd-admin service exposes the admin gRPC API over SSH (default port 2223). You can manage active sessions using subcommands. In the demo environment, the service is started with --no-auth to allow any client to connect.

    Subcommands

    • list: Lists all active sessions.
    • stream <session-id>: Streams a live session as raw bytes (use Ctrl-C to stop).
    • kill <session-id>: Terminates a specific session.
    • Interactive Shell: Connecting without a subcommand opens an interactive shell where these commands are also available.
  11. Configure SSH keys and private keys

    master

    To use SSH key authentication:

    • Downstream Authentication (from.authorized_keys): Provide a single file path or a list of file paths in standard authorized_keys format. This allows downstream clients to authenticate via their public keys.
    • Upstream Authentication (to.private_key): Provide the path to the private key used to connect to the upstream server.

    Important Considerations:

    • Passphrases: Do not use a passphrase with to.private_key, as it causes a degraded user experience with multiple prompts.
    • Host Key Negotiation: If the upstream server offers multiple host keys (e.g., RSA, ECDSA, Ed25519), ensure your known_hosts configuration includes the specific key type sshpiper attempts to verify, otherwise connection may fail with Permission denied (publickey).
    • Upstream Certificates: sshpiper does not support connecting to upstreams via SSH user certificates; the upstream must trust the public key associated with to.private_key.
    # yaml-language-server: $schema=https://raw.githubusercontent.com/tg123/sshpiper/master/plugin/yaml/schema.json
    version: "1.0"
    pipes:
    - from:
        - username: "hello"
          authorized_keys:
          - /path/to/authorized_keys
          - /path/to/authorized_keys2
      to:
        host: example.com:22
        username: "world"
        private_key: /path/to/id_rsa