| 1 | # P2P Tunnels |
| 2 | |
| 3 | Kubo supports tunneling TCP connections through libp2p streams, similar to SSH |
| 4 | port forwarding (`ssh -L`). This allows exposing local services to remote peers |
| 5 | and forwarding remote services to local ports. |
| 6 | |
| 7 | - [Why P2P Tunnels?](#why-p2p-tunnels) |
| 8 | - [Quick Start](#quick-start) |
| 9 | - [Background Mode](#background-mode) |
| 10 | - [Foreground Mode](#foreground-mode) |
| 11 | - [systemd Integration](#systemd-integration) |
| 12 | - [Security Considerations](#security-considerations) |
| 13 | - [Troubleshooting](#troubleshooting) |
| 14 | |
| 15 | ## Why P2P Tunnels? |
| 16 | |
| 17 | Unlike traditional SSH tunnels, libp2p-based tunnels do not require: |
| 18 | |
| 19 | - **No public IP or open ports**: The server does not need a static IP address |
| 20 | or port forwarding configured on the router. Connectivity to peers behind NAT |
| 21 | is facilitated by [Direct Connection Upgrade through Relay (DCUtR)](https://github.com/libp2p/specs/blob/master/relay/DCUtR.md), |
| 22 | which enables NAT hole-punching. |
| 23 | |
| 24 | - **No DNS or IP address management**: All you need is the server's PeerID and |
| 25 | an agreed-upon protocol name (e.g., `/x/ssh`). Kubo handles peer discovery |
| 26 | and routing via the [Amino DHT](https://specs.ipfs.tech/routing/kad-dht/). |
| 27 | |
| 28 | - **Simplified firewall rules**: Since connections are established through |
| 29 | libp2p's existing swarm connections, no additional firewall configuration is |
| 30 | needed beyond what Kubo already requires. |
| 31 | |
| 32 | This makes p2p tunnels useful for connecting to machines on home networks, |
| 33 | behind corporate firewalls, or in environments where traditional port forwarding |
| 34 | is not available. |
| 35 | |
| 36 | ## Quick Start |
| 37 | |
| 38 | Enable the experimental feature: |
| 39 | |
| 40 | ```console |
| 41 | $ ipfs config --json Experimental.Libp2pStreamMounting true |
| 42 | ``` |
| 43 | |
| 44 | Test with netcat (`nc`) - no services required: |
| 45 | |
| 46 | **On the server:** |
| 47 | |
| 48 | ```console |
| 49 | $ ipfs p2p listen /x/test /ip4/127.0.0.1/tcp/9999 |
| 50 | $ nc -l -p 9999 |
| 51 | ``` |
| 52 | |
| 53 | **On the client:** |
| 54 | |
| 55 | Replace `$SERVER_ID` with the server's peer ID (get it with `ipfs id -f "<id>\n"` |
| 56 | on the server). |
| 57 | |
| 58 | ```console |
| 59 | $ ipfs p2p forward /x/test /ip4/127.0.0.1/tcp/9998 /p2p/$SERVER_ID |
| 60 | $ nc 127.0.0.1 9998 |
| 61 | ``` |
| 62 | |
| 63 | Type in either terminal and the text appears in the other. Use Ctrl+C to exit. |
| 64 | |
| 65 | ## Background Mode |
| 66 | |
| 67 | By default, `ipfs p2p listen` and `ipfs p2p forward` register the tunnel with |
| 68 | the daemon and return immediately. The tunnel persists until explicitly closed |
| 69 | with `ipfs p2p close` or the daemon shuts down. |
| 70 | |
| 71 | This example exposes a local SSH server (listening on `localhost:22`) to a |
| 72 | remote peer. The same pattern works for any TCP service. |
| 73 | |
| 74 | **On the server** (the machine running SSH): |
| 75 | |
| 76 | Register a p2p listener that forwards incoming connections to the local SSH |
| 77 | server. The protocol name `/x/ssh` is an arbitrary identifier that both peers |
| 78 | must agree on (the `/x/` prefix is required for custom protocols). |
| 79 | |
| 80 | ```console |
| 81 | $ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 |
| 82 | ``` |
| 83 | |
| 84 | **On the client:** |
| 85 | |
| 86 | Create a local port (`2222`) that tunnels through libp2p to the server's SSH |
| 87 | service. |
| 88 | |
| 89 | ```console |
| 90 | $ ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$SERVER_ID |
| 91 | ``` |
| 92 | |
| 93 | Now connect to SSH through the tunnel: |
| 94 | |
| 95 | ```console |
| 96 | $ ssh user@127.0.0.1 -p 2222 |
| 97 | ``` |
| 98 | |
| 99 | **Other services:** To tunnel a different service, change the port and protocol |
| 100 | name. For example, to expose a web server on port 8080, use `/x/mywebapp` and |
| 101 | `/ip4/127.0.0.1/tcp/8080`. |
| 102 | |
| 103 | ## Foreground Mode |
| 104 | |
| 105 | Use `--foreground` (`-f`) to block until interrupted. The tunnel is |
| 106 | automatically removed when the command exits: |
| 107 | |
| 108 | ```console |
| 109 | $ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 --foreground |
| 110 | Listening on /x/ssh, forwarding to /ip4/127.0.0.1/tcp/22, waiting for interrupt... |
| 111 | ^C |
| 112 | Received interrupt, removing listener for /x/ssh |
| 113 | ``` |
| 114 | |
| 115 | The listener/forwarder is automatically removed when: |
| 116 | |
| 117 | - The command receives Ctrl+C or SIGTERM |
| 118 | - `ipfs p2p close` is called |
| 119 | - The daemon shuts down |
| 120 | |
| 121 | This mode is useful for systemd services and scripts that need cleanup on exit. |
| 122 | |
| 123 | ### systemd Integration |
| 124 | |
| 125 | The `--foreground` flag enables clean integration with systemd. The examples |
| 126 | below show how to run `ipfs p2p listen` as a user service that starts |
| 127 | automatically when the IPFS daemon is ready. |
| 128 | |
| 129 | Ensure IPFS daemon runs as a systemd user service. See |
| 130 | [misc/README.md](https://github.com/ipfs/kubo/blob/master/misc/README.md#systemd) |
| 131 | for setup instructions and where to place unit files. |
| 132 | |
| 133 | #### P2P listener with path-based activation |
| 134 | |
| 135 | Use a `.path` unit to wait for the daemon's RPC API to be ready before starting |
| 136 | the p2p listener. |
| 137 | |
| 138 | **`ipfs-p2p-tunnel.path`**: |
| 139 | |
| 140 | ```systemd |
| 141 | [Unit] |
| 142 | Description=Monitor for IPFS daemon startup |
| 143 | After=ipfs.service |
| 144 | Requires=ipfs.service |
| 145 | |
| 146 | [Path] |
| 147 | PathExists=%h/.ipfs/api |
| 148 | Unit=ipfs-p2p-tunnel.service |
| 149 | |
| 150 | [Install] |
| 151 | WantedBy=default.target |
| 152 | ``` |
| 153 | |
| 154 | The `%h` specifier expands to the user's home directory. If you use a custom |
| 155 | `IPFS_PATH`, adjust accordingly. |
| 156 | |
| 157 | **`ipfs-p2p-tunnel.service`**: |
| 158 | |
| 159 | ```systemd |
| 160 | [Unit] |
| 161 | Description=IPFS p2p tunnel |
| 162 | Requires=ipfs.service |
| 163 | |
| 164 | [Service] |
| 165 | ExecStart=ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 -f |
| 166 | Restart=on-failure |
| 167 | RestartSec=10 |
| 168 | |
| 169 | [Install] |
| 170 | WantedBy=default.target |
| 171 | ``` |
| 172 | |
| 173 | #### Enabling the services |
| 174 | |
| 175 | ```console |
| 176 | $ systemctl --user enable ipfs.service |
| 177 | $ systemctl --user enable ipfs-p2p-tunnel.path |
| 178 | $ systemctl --user start ipfs.service |
| 179 | ``` |
| 180 | |
| 181 | The path unit monitors `~/.ipfs/api` and starts `ipfs-p2p-tunnel.service` |
| 182 | once the file exists. |
| 183 | |
| 184 | ## Security Considerations |
| 185 | |
| 186 | > [!WARNING] |
| 187 | > This feature provides CLI and HTTP RPC users with the ability to set up port |
| 188 | > forwarding for localhost and LAN ports. If you enable this and plan to expose |
| 189 | > CLI or HTTP RPC to other users or machines, secure the RPC API using |
| 190 | > [`API.Authorizations`](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations) |
| 191 | > or custom auth middleware. |
| 192 | |
| 193 | ## Troubleshooting |
| 194 | |
| 195 | ### Foreground listener stops when terminal closes |
| 196 | |
| 197 | When using `--foreground`, the listener stops if the terminal closes. For |
| 198 | persistent foreground listeners, use a systemd service, `nohup`, `tmux`, or |
| 199 | `screen`. Without `--foreground`, the listener persists in the daemon regardless |
| 200 | of terminal state. |
| 201 | |
| 202 | ### Connection refused errors |
| 203 | |
| 204 | Verify: |
| 205 | |
| 206 | 1. The experimental feature is enabled: `ipfs config Experimental.Libp2pStreamMounting` |
| 207 | 2. The listener is active: `ipfs p2p ls` |
| 208 | 3. Both peers can connect: `ipfs swarm connect /p2p/$PEER_ID` |
| 209 | |
| 210 | ### Persistent tunnel configuration |
| 211 | |
| 212 | There is currently no way to define tunnels in the Kubo JSON config file. Use |
| 213 | `--foreground` mode with a systemd service for persistent tunnels. Support for |
| 214 | configuring tunnels via JSON config may be added in the future (see [kubo#5460](https://github.com/ipfs/kubo/issues/5460) - PRs welcome!). |