A fast TCP/UDP tunnel, transported over HTTP WebSocket.
You are right. This project is inspired by jpillora/chisel (and subsequently
my fork myzhang1029/penguin), but completely rewritten in Rust without any
linkage to chisel. The logo is generated by DALL-E
with the prompt "a penguin standing behind a gear wheel, digital art, logo."
Prebuilt binaries are available for the latest commit on main:
- On the Releases page, under nightly releases.
- On ZipZen nightly releases.
ZipZen kindly hosts DEB and RPM packages for us.
Debian/Ubuntu configuration:
sudo curl -fsSLo /usr/share/keyrings/zipzen-rusty-penguin.gpg https://apt.zipzen.dev/mzh/rusty-penguin/pub.gpg
sudo tee /etc/apt/sources.list.d/zipzen-rusty-penguin.sources << EOF
Types: deb
URIs: https://apt.zipzen.dev/mzh/rusty-penguin
Suites: stable
Components: main
Signed-By: /usr/share/keyrings/zipzen-rusty-penguin.gpg
EOF
sudo apt updateRPM configuration:
sudo tee /etc/yum.repos.d/zipzen-rusty-penguin.repo << EOF
[rusty-penguin]
name=rusty-penguin Packages
baseurl=https://yum.zipzen.dev/mzh/rusty-penguin/9/\$basearch/
enabled=1
gpgcheck=1
gpgkey=https://yum.zipzen.dev/mzh/rusty-penguin/RPM-GPG-KEY
EOFThe correct signing key is currently
pub rsa4096/0x161B8E07A27FC919 2025-10-25 [SC] [expires: 2026-10-25]
493DBFE421EAA3FEEA1C393F161B8E07A27FC919
uid Zhang Maiyun (ZipZen Release Signing Key) <zipzen@maiyun.me>
$ penguin server --host ::1 --port 443 --tls-cert cert.pem --tls-key key.pem --ws-psk some-secretSee penguin server --help for more options.
$ penguin client --ws-psk some-secret wss://server 1080:socks 80:example.com:80See penguin client --help for more options.
Compared to the original penguin or chisel, this project stripped away
some functionalities:
-
There is no internal SSH tunnels because it results in double encapsulation when used with HTTPS/WSS.
-
There is no user/password authentication because we do not have SSH. Instead, use PSK authentication.
-
There is no server keep-alive because client keep-alive is enough.
-
There is no support to acquire an ACME certificate on-the-fly.(Implemented) -
There is no reverse port forwarding because I am too lazy.(Implemented)
Other than that, this project offers these functionalities compared to
chisel:
-
Plausible deniability with WebSocket PSK and working
backend. -
TLS certificate hot-reload with
SIGUSR1. -
Higher performance: my crude testing on my machine reveals that
penguinis approximately 2x faster thanchiselon my machine (penguincommit73a0045ffvschiselcommitab8f06a8).
$ iperf3 -c 127.0.0.1 # chisel without TLS
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.00 sec 5.41 GBytes 4.65 Gbits/sec sender
[ 5] 0.00-10.00 sec 5.40 GBytes 4.64 Gbits/sec receiver
$ iperf3 -c 127.0.0.1 # penguin without TLS
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.00 sec 16.5 GBytes 14.2 Gbits/sec sender
[ 5] 0.00-10.00 sec 16.5 GBytes 14.2 Gbits/sec receiver
$ iperf3 -c 127.0.0.1 # chisel with TLS
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.00 sec 4.79 GBytes 4.12 Gbits/sec sender
[ 5] 0.00-10.01 sec 4.79 GBytes 4.11 Gbits/sec receiver
$ iperf3 -c 127.0.0.1 # penguin with TLS
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.01 sec 11.0 GBytes 9.48 Gbits/sec sender
[ 5] 0.00-10.01 sec 11.0 GBytes 9.48 Gbits/sec receiver
- All the safety Rust offers.
Servers and clients with the same protocol version are compatible with each other. However, for the best performance, it is recommended to use the same version of penguin on both sides.
The current protocol version is penguin-v7. See PROTOCOL.md for details.
Common features:
nohash: (caution) usenohash_hasheras the internalflow_idhashmap. This option may be an optimization for resource-constrained devices, but will also open up a DoS attack vector if the peer cannot be trusted. If both peers use this penguin implementation or any other implementation that generatesflow_ids with a random number generator, this is safe.
Library features:
tungstenite: implement our traits ontokio_tungstenite::WebSocketStream(default)
Executable features:
-
client: build the client (default) -
server: build the server (default) -
penguin-binary: shorthand for bothserverandclient(default) -
rustls-native-roots: userustlswith system CA (default) -
rustls-webpki-roots: userustlswith bundled webpki CA -
nativetls: usenative-tls -
ring: useringas the crypto provider forrustls -
aws-lc-rs: useaws-lc-rsas the crypto provider forrustls -
default-is-ipv6: use::/::1instead of0.0.0.0/127.0.0.1when an IP address is omitted in the client command line -
tokio-console: enableconsole-subscribersupport -
remove-logging: statically removetracelevel logging and tracing code -
deadlock-detection: spawn a background thread runningparking_lot's deadlock detection -
acme: (requiresserver) enable the built-in ACME client (default) Will also make the binary userustlseven ifnativetlsis enabled due to internal dependencies. -
rustls-keylog: (caution) export TLS session data to the file specified in the environmental variableSSLKEYLOGFILE
Testing features:
tests-real-internet4: run tests that require IPv4 access to the internettests-real-internet6: run tests that require IPv4 access to the internettests-udp: run tests that expect UDP traffic to work reliably. They may be flaky depending on the network environment.tests-acme-has-pebble: test the ACME client with a local ACME server athttps://localhost:14000/dir
All contributions are welcome. Please make sure you
- Write test cases for the bugfix/feature
- Check that the patch passes all tests by running
cargo test
- Send a Pull Request.
GPL v3.0 or later or Apache License 2.0.
