* feat(p2p): add --foreground flag to listen and forward commands
adds `-f/--foreground` option that keeps the command running until
interrupted (SIGTERM/Ctrl+C) or closed via `ipfs p2p close`. the
listener/forwarder is automatically removed when the command exits.
useful for systemd services and scripts that need cleanup on exit.
* docs: add p2p-tunnels.md with systemd examples
- add dedicated docs/p2p-tunnels.md covering:
- why p2p tunnels (NAT traversal, no public IP needed)
- quick start with netcat
- background and foreground modes
- systemd integration with path-based activation
- security considerations and troubleshooting
- document Experimental.Libp2pStreamMounting in docs/config.md
- simplify docs/experimental-features.md, link to new doc
- add "Learn more" links to ipfs p2p listen/forward --help
- update changelog entry with doc link
- add cross-reference in misc/README.md
* chore: reference kubo#5460 for p2p config
Ref. https://github.com/ipfs/kubo/issues/5460
* fix(daemon): write api/gateway files only after HTTP server is ready
fixes race condition where $IPFS_PATH/api and $IPFS_PATH/gateway files
were written before the HTTP servers were ready to accept connections.
this caused issues for tools like systemd path units that immediately
try to connect when these files appear.
changes:
- add corehttp.ServeWithReady() that signals when server is ready
- wait for ready signal before writing address files
- use sync.WaitGroup.Go() (Go 1.25) for cleaner goroutine management
- add TestAddressFileReady to verify both api and gateway files
* fix(daemon): buffer errc channel and wait for all listeners
- buffer error channel with len(listeners) to prevent deadlock when
multiple servers write errors simultaneously
- wait for ALL listeners to be ready before writing api/gateway file,
not just the first one
Feedback-from: https://github.com/ipfs/kubo/pull/11099#pullrequestreview-3593885839
* docs(changelog): improve p2p tunnel section clarity
reframe to lead with user benefit and add example output
* docs(p2p): remove obsolete race condition caveat
the "First launch fails but restarts work" troubleshooting section
described a race where the api file was written before the daemon was
ready. this was fixed in 80b703a which ensures api/gateway files are
only written after HTTP servers are ready to accept connections.
---------
Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com>
6.4 KiB
P2P Tunnels
Kubo supports tunneling TCP connections through libp2p streams, similar to SSH
port forwarding (ssh -L). This allows exposing local services to remote peers
and forwarding remote services to local ports.
- Why P2P Tunnels?
- Quick Start
- Background Mode
- Foreground Mode
- Security Considerations
- Troubleshooting
Why P2P Tunnels?
Unlike traditional SSH tunnels, libp2p-based tunnels do not require:
-
No public IP or open ports: The server does not need a static IP address or port forwarding configured on the router. Connectivity to peers behind NAT is facilitated by Direct Connection Upgrade through Relay (DCUtR), which enables NAT hole-punching.
-
No DNS or IP address management: All you need is the server's PeerID and an agreed-upon protocol name (e.g.,
/x/ssh). Kubo handles peer discovery and routing via the Amino DHT. -
Simplified firewall rules: Since connections are established through libp2p's existing swarm connections, no additional firewall configuration is needed beyond what Kubo already requires.
This makes p2p tunnels useful for connecting to machines on home networks, behind corporate firewalls, or in environments where traditional port forwarding is not available.
Quick Start
Enable the experimental feature:
$ ipfs config --json Experimental.Libp2pStreamMounting true
Test with netcat (nc) - no services required:
On the server:
$ ipfs p2p listen /x/test /ip4/127.0.0.1/tcp/9999
$ nc -l -p 9999
On the client:
Replace $SERVER_ID with the server's peer ID (get it with ipfs id -f "<id>\n"
on the server).
$ ipfs p2p forward /x/test /ip4/127.0.0.1/tcp/9998 /p2p/$SERVER_ID
$ nc 127.0.0.1 9998
Type in either terminal and the text appears in the other. Use Ctrl+C to exit.
Background Mode
By default, ipfs p2p listen and ipfs p2p forward register the tunnel with
the daemon and return immediately. The tunnel persists until explicitly closed
with ipfs p2p close or the daemon shuts down.
This example exposes a local SSH server (listening on localhost:22) to a
remote peer. The same pattern works for any TCP service.
On the server (the machine running SSH):
Register a p2p listener that forwards incoming connections to the local SSH
server. The protocol name /x/ssh is an arbitrary identifier that both peers
must agree on (the /x/ prefix is required for custom protocols).
$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22
On the client:
Create a local port (2222) that tunnels through libp2p to the server's SSH
service.
$ ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$SERVER_ID
Now connect to SSH through the tunnel:
$ ssh user@127.0.0.1 -p 2222
Other services: To tunnel a different service, change the port and protocol
name. For example, to expose a web server on port 8080, use /x/mywebapp and
/ip4/127.0.0.1/tcp/8080.
Foreground Mode
Use --foreground (-f) to block until interrupted. The tunnel is
automatically removed when the command exits:
$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 --foreground
Listening on /x/ssh, forwarding to /ip4/127.0.0.1/tcp/22, waiting for interrupt...
^C
Received interrupt, removing listener for /x/ssh
The listener/forwarder is automatically removed when:
- The command receives Ctrl+C or SIGTERM
ipfs p2p closeis called- The daemon shuts down
This mode is useful for systemd services and scripts that need cleanup on exit.
systemd Integration
The --foreground flag enables clean integration with systemd. The examples
below show how to run ipfs p2p listen as a user service that starts
automatically when the IPFS daemon is ready.
Ensure IPFS daemon runs as a systemd user service. See misc/README.md for setup instructions and where to place unit files.
P2P listener with path-based activation
Use a .path unit to wait for the daemon's RPC API to be ready before starting
the p2p listener.
ipfs-p2p-tunnel.path:
[Unit]
Description=Monitor for IPFS daemon startup
After=ipfs.service
Requires=ipfs.service
[Path]
PathExists=%h/.ipfs/api
Unit=ipfs-p2p-tunnel.service
[Install]
WantedBy=default.target
The %h specifier expands to the user's home directory. If you use a custom
IPFS_PATH, adjust accordingly.
ipfs-p2p-tunnel.service:
[Unit]
Description=IPFS p2p tunnel
Requires=ipfs.service
[Service]
ExecStart=ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 -f
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
Enabling the services
$ systemctl --user enable ipfs.service
$ systemctl --user enable ipfs-p2p-tunnel.path
$ systemctl --user start ipfs.service
The path unit monitors ~/.ipfs/api and starts ipfs-p2p-tunnel.service
once the file exists.
Security Considerations
Warning
This feature provides CLI and HTTP RPC users with the ability to set up port forwarding for localhost and LAN ports. If you enable this and plan to expose CLI or HTTP RPC to other users or machines, secure the RPC API using
API.Authorizationsor custom auth middleware.
Troubleshooting
Foreground listener stops when terminal closes
When using --foreground, the listener stops if the terminal closes. For
persistent foreground listeners, use a systemd service, nohup, tmux, or
screen. Without --foreground, the listener persists in the daemon regardless
of terminal state.
Connection refused errors
Verify:
- The experimental feature is enabled:
ipfs config Experimental.Libp2pStreamMounting - The listener is active:
ipfs p2p ls - Both peers can connect:
ipfs swarm connect /p2p/$PEER_ID
Persistent tunnel configuration
There is currently no way to define tunnels in the Kubo JSON config file. Use
--foreground mode with a systemd service for persistent tunnels. Support for
configuring tunnels via JSON config may be added in the future (see kubo#5460 - PRs welcome!).