Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Dead simple, jargon-free Python tool to make a local TCP port available on a remote host or make a remote TCP port available locally, with auto-reconnect.

This is a Python reimplementation of push-pull-port that uses paramiko instead of shelling out to autossh, making it cross-platform and self-contained.

Motivation

Modern work is mobile. Whether you're at home, in a cafe, or on the move with 4G, you need secure, on-demand access to your devices and services - without wrestling with complex forwarding rules or VPNs.

sshpushpull lets you instantly "push" or "pull" any TCP port via SSH with human-friendly commands.

  • Expose your device's SSH, VNC, or web apps at a moment's notice.
  • Stop and start tunnels with a single command.
  • Failures show up immediately - no silent, sneaky background errors.
  • Auto-reconnects after transient network drops with exponential backoff.

It's the Unix philosophy, everywhere: portable, composable, and under your control.

Prerequisites

  • Python 2 or Python 3
  • paramiko (installed automatically via pip install sshpushpull)
  • SSH access to the remote host
  • For unattended tunnels, set up SSH key-based authentication so you're not prompted for a password on every reconnect.

Note: When using push, to make the pushed port accessible from other hosts, ensure the remote SSH server has GatewayPorts clientspecified set in /etc/ssh/sshd_config.

Installation

pip install sshpushpull

Usage

sshpushpull provides two subcommands:

  • push — Make a local TCP port available on a remote host (equivalent to ssh -R)
  • pull — Make a remote TCP port available locally (equivalent to ssh -L)

Both commands run in the foreground and auto-reconnect after transient network failures with exponential backoff (up to 10 seconds).

Push: Expose a local port on a remote host

sshpushpull push \
    --local-port <local_port> \
    --remote-port <remote_port> \
    --username <user> \
    --host <host> \
    [--port <ssh_port>] \
    [--password <pwd> | --rsa-key <path> | --ed25519-key <path>] \
    [--localhost-only]
Option Description
--local-port Local TCP port to push from (repeatable for multiple ports)
--remote-port Remote TCP port to push to (repeatable for multiple ports)
--username SSH username on the remote host
--host Remote SSH host name or address
--port SSH server port (default: 22)
--password Password for SSH authentication
--rsa-key Path to RSA private key for SSH authentication
--ed25519-key Path to Ed25519 private key for SSH authentication
--localhost-only Open remote port on localhost only (default: open on all interfaces)

Examples:

# Make local port 3000 available on dev.example.com:3001 using password auth
sshpushpull push --local-port 3000 --remote-port 3001 --username dev --host dev.example.com --password secret

# Using an Ed25519 key
sshpushpull push --local-port 3000 --remote-port 3001 --username dev --host dev.example.com --ed25519-key ~/.ssh/id_ed25519

# If SSH is running on port 2222
sshpushpull push --local-port 3000 --remote-port 3001 --username dev --host dev.example.com --port 2222 --rsa-key ~/.ssh/id_rsa

# Push multiple ports at once
sshpushpull push \
    --host dev.example.com \
    --username dev \
    --ed25519-key ~/.ssh/id_ed25519 \
    --local-port 3000 --remote-port 3001 \
    --local-port 4000 --remote-port 4001 \
    --local-port 8080 --remote-port 9090

# Only allow access from the remote host's own localhost
sshpushpull push --local-port 3000 --remote-port 3001 --username dev --host dev.example.com --ed25519-key ~/.ssh/id_ed25519 --localhost-only

Pull: Access a remote port locally

sshpushpull pull \
    --remote-port <remote_port> \
    --local-port <local_port> \
    --username <user> \
    --host <host> \
    [--port <ssh_port>] \
    [--password <pwd> | --rsa-key <path> | --ed25519-key <path>]
Option Description
--remote-port Remote TCP port to pull from (repeatable for multiple ports)
--local-port Local TCP port to pull to (repeatable for multiple ports)
--username SSH username on the remote host
--host Remote SSH host name or address
--port SSH server port (default: 22)
--password Password for SSH authentication
--rsa-key Path to RSA private key for SSH authentication
--ed25519-key Path to Ed25519 private key for SSH authentication

Examples:

# Access remote port 3306 (database) through local port 3307
sshpushpull pull --remote-port 3306 --local-port 3307 --username admin --host db.internal --password secret

# Using an Ed25519 key
sshpushpull pull --remote-port 3306 --local-port 3307 --username admin --host db.internal --ed25519-key ~/.ssh/id_ed25519

# Pull multiple ports at once
sshpushpull pull \
    --host db.internal \
    --username admin \
    --password secret \
    --local-port 3307 --remote-port 3306 \
    --local-port 5433 --remote-port 5432

# If SSH is running on port 2222
sshpushpull pull --remote-port 3306 --local-port 3307 --username admin --host db.internal --port 2222 --rsa-key ~/.ssh/id_rsa

Multiple Ports

Both push and pull support forwarding multiple ports over a single SSH connection. Simply repeat --local-port and --remote-port in matched pairs:

# Push: expose three local services on the remote host
sshpushpull push \
    --host dev.example.com \
    --username dev \
    --ed25519-key ~/.ssh/id_ed25519 \
    --local-port 3000 --remote-port 3001 \
    --local-port 4000 --remote-port 4001 \
    --local-port 8080 --remote-port 9090

# Pull: access remote database and cache locally
sshpushpull pull \
    --host db.internal \
    --username admin \
    --password secret \
    --local-port 3307 --remote-port 3306 \
    --local-port 6380 --remote-port 6379

Ports are paired by position: the first --local-port with the first --remote-port, the second with the second, and so on. All mappings share a single SSH connection and reconnect together.

Foreground Operation: Visibility Over Stealth

We intentionally run all tunnels in the foreground. This ensures:

  • Immediate Error Visibility: Any connection issues, authentication failures, or port conflicts are clearly printed to your terminal, so you can respond and debug without guessing.
  • No Silent Failures: By avoiding background daemons, you won't miss subtle (or catastrophic) tunnel dropouts that go unnoticed.
  • Stopping the tunnel: Simply press Ctrl-C to stop the tunnel. You can also close the terminal window/tab to end it.

Tip: If you ever want to run the tunnel in the background, you can use a terminal multiplexer like tmux to keep tunnels running while detached.

Auto-Reconnect

Both push and pull automatically reconnect after transient network failures. The reconnect strategy uses exponential backoff:

  1. First retry: 1 second
  2. Second retry: 2 seconds
  3. Third retry: 4 seconds
  4. ...up to a maximum of 10 seconds

After a successful reconnection, the backoff resets to 1 second.

Why We Use Push/Pull Instead of Forward/Reverse

The Problem with Traditional Terms

The standard SSH port forwarding terms (local forwarding vs remote forwarding) are notoriously confusing because:

  1. Perspective Dependence
    The "remote" and "local" labels depend on which machine initiates the SSH connection, not the actual service exposure direction users care about.

  2. Cognitive Mismatch
    When developers want to:

    • Expose a local service remotely, they must remember this is called "remote forwarding" (-R)
    • Access a remote service locally, this is called "local forwarding" (-L)
  3. Implementation-First Naming
    The terms describe SSH's technical implementation rather than user intent.

Our Push/Pull Metaphor

We intentionally avoid forward/reverse terminology in favor of intuitive action verbs:

User Goal Traditional Term Our Term SSH Option
Make local service available on remote host Remote Forwarding Push ssh -R
Access remote service through local port Local Forwarding Pull ssh -L

Key Advantages

  1. Intent-Oriented

    • push: "I want to make this local port available there"
    • pull: "I want to access that remote port here"
  2. Directionally Clear
    Eliminates ambiguity about "whose local/remote" we're referring to.

  3. Cloud-Native Alignment
    Matches modern service mesh concepts (ingress/egress) better than SSH's 1990s perspective.

Contributing

Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.

License

This project is licensed under the MIT License.

Release files for sshpushpull 0.1.0a3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sshpushpull 0.1.0a3
File Size Uploaded
sshpushpull-0.1.0a3.tar.gz 10.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sshpushpull 0.1.0a3
File Interpreter ABI Platform
sshpushpull-0.1.0a3-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 21.7 kB

Release files / sshpushpull-0.1.0a3.tar.gz

Download URL sshpushpull-0.1.0a3.tar.gz
Size 10.7 kB
Tags Source
SHA-256 checksum
How to use checksums
47cb85fe1b2e9e2d1c1df19b7ccad1fcfb9fb8838d6e116ade0d232f9accb3ae
BLAKE2b-256 checksum
How to use checksums
6a93d47d9609b733564922bc11beff83f1184c58f3f4f2ded01dd7d1f17088db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / sshpushpull-0.1.0a3-py2.py3-none-any.whl

Download URL sshpushpull-0.1.0a3-py2.py3-none-any.whl
Size 11.0 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
aadb339b9dcdec948dcd7ec863807772f2ee5627f7cc12696f954920528efa5a
BLAKE2b-256 checksum
How to use checksums
3db394e409d221fdde010dc75b5d22814104c737cd1a4f16eb4dfd9872ad4706
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page