From b85b4e1b7e2fa1bcf4ec809c2115102084a61671 Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:31:38 -0400 Subject: [PATCH 1/6] Update DevSite articles to match latest versions This pull request updates several documentation articles to synchronize them with the latest versions available internally. Some articles had become outdated, and these changes bring in the most current updates. --- docs/vpn/advanced/prefixing.md | 51 +++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/docs/vpn/advanced/prefixing.md b/docs/vpn/advanced/prefixing.md index ef26f6bb..c2e7ecfb 100644 --- a/docs/vpn/advanced/prefixing.md +++ b/docs/vpn/advanced/prefixing.md @@ -1,13 +1,11 @@ ---- -title: "Disguise Connections with Prefixes" -sidebar_label: "Connection Prefixes" ---- +Project: /outline/_project.yaml +Book: /outline/_book.yaml # Connection Prefix Disguises As of Outline Client version 1.9.0, access keys support the "prefix" option. The "prefix" is a list of bytes used as the first bytes of the -[salt](https://shadowsocks.org/doc/aead.html) of a Shadowsocks TCP connection. +[salt](https://shadowsocks.org/guide/aead.html) of a Shadowsocks connection. This can make the connection look like a protocol that is allowed in the network, circumventing firewalls that reject protocols they don't recognize. @@ -27,9 +25,9 @@ The port you use should match the protocol that your prefix is pretending to be. IANA keeps a [transport protocol port number registry](https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml) that maps protocols and port numbers. -Some examples of effective prefixes look like common protocols: +Some examples of effective TCP prefixes that look like common protocols: -| | Recommended Port | JSON-encoded | URL-encoded | +| | Recommended Port | YAML-encoded | URL-encoded | |----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|--------------------------| | HTTP request | 80 (http) | `"POST "` | `POST%20` | | HTTP response | 80 (http) | `"HTTP/1.1 "` | `HTTP%2F1.1%20` | @@ -39,22 +37,35 @@ Some examples of effective prefixes look like common protocols: | TLS ServerHello | 443 (https), 463 (smtps), 563 (nntps), 636 (ldaps), 989 (ftps-data), 990 (ftps), 993 (imaps), 995 (pop3s), 5223 (Apple APN), 5228 (Play Store), 5349 (turns) | `"\u0016\u0003\u0003\u0040\u0000\u0002"` | `%16%03%03%40%00%02` | | SSH | 22 (ssh), 830 (netconf-ssh), 4334 (netconf-ch-ssh), 5162 (snmpssh-trap) | `"SSH-2.0\r\n"` | `SSH-2.0%0D%0A` | +Some examples of effective UDP prefixes that look like common protocols: + +| | Recommended Port | YAML-encoded | +|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------| +| DNS request | 53 (dns) | `"\u006b\u007b\u0001\u0020"` (note: randomize the first two bytes) | +| DNS response | 53 (dns) | `"\u006b\u007b\u0081\u00a0\u0000\u0001"` (note: randomize the first two bytes) | +| QUIC Client Initial | 443 (https) | `"\u00cd\u0000\u0000\u0000\u0001"` | + ### Dynamic Access Keys -To use the prefix feature with [Dynamic Access Keys](../management/dynamic-access-keys.md) (`ssconf://`), -add a "prefix" key to the JSON object, with a **JSON-encoded** value -representing the prefix you want (see examples in the table above). You can +To use the prefix feature with [Dynamic Access Keys](/outline/docs/guides/service-providers/dynamic-access-keys.md) (`ssconf://`), +add a "prefix" key to the YAML object, with a **YAML-encoded** value +representing the prefix you want_ (see examples in the table above)_. You can use escape codes (like \u00FF) to represent non-printable Unicode codepoints in the `U+0` to `U+FF` range. For example: -```json -{ - "server": "example.com", - "server_port": 8388, - "password": "example", - "method": "chacha20-ietf-poly1305", - "prefix": "\u0005\u00DC\u005F\u00E0\u0001\u0020" -} +```yaml +transport: + $type: tcpudp + tcp: + <<: &shared + $type: shadowsocks + endpoint: 147.182.248.224:20478 + secret: cqXYJ2BtMyNHneQHjpIXyg + cipher: chacha20-ietf-poly1305 + prefix: "\u0013\u0003\u0003\u003F" + udp: + <<: *shared + prefix: "\u006b\u007b\u0001\u0020" ``` ### Static Access Keys @@ -66,6 +77,8 @@ of these in the table above) and add it to the end of the access key like so: `ss://Z34nthataITHiTNIHTohithITHbVBqQ1o3bkk@127.0.0.1:33142/?outline=1&prefix=` +Prefixes in the URL format only work for TCP connections. + For advanced users, you can use your browser's `encodeURIComponent()` function to convert your **JSON-encoded** prefix to a **URL-encoded** one. To do this, open your web inspector console @@ -80,4 +93,4 @@ Press enter. The value produced will be the *URL-encoded *version. For example: ```js encodeURIComponent("\u0016\u0003\u0001\u0000\u00a8\u0001\u0001") '%16%03%01%00%C2%A8%01%01' -``` \ No newline at end of file +``` From e2f800349dc5a8199182ce98dc9ea51bcee9ae6c Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:32:51 -0400 Subject: [PATCH 2/6] Revert unnecessary changes --- docs/vpn/advanced/prefixing.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/vpn/advanced/prefixing.md b/docs/vpn/advanced/prefixing.md index c2e7ecfb..80efdc00 100644 --- a/docs/vpn/advanced/prefixing.md +++ b/docs/vpn/advanced/prefixing.md @@ -1,5 +1,7 @@ -Project: /outline/_project.yaml -Book: /outline/_book.yaml +--- +title: "Disguise Connections with Prefixes" +sidebar_label: "Connection Prefixes" +--- # Connection Prefix Disguises From 9f7525d6d576ec48e0affb57fe805b984a2fb596 Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:33:47 -0400 Subject: [PATCH 3/6] Fix link to Shadowsocks salt documentation --- docs/vpn/advanced/prefixing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vpn/advanced/prefixing.md b/docs/vpn/advanced/prefixing.md index 80efdc00..ed475fd9 100644 --- a/docs/vpn/advanced/prefixing.md +++ b/docs/vpn/advanced/prefixing.md @@ -7,7 +7,7 @@ sidebar_label: "Connection Prefixes" As of Outline Client version 1.9.0, access keys support the "prefix" option. The "prefix" is a list of bytes used as the first bytes of the -[salt](https://shadowsocks.org/guide/aead.html) of a Shadowsocks connection. +[salt](https://shadowsocks.org/doc/aead.html) of a Shadowsocks connection. This can make the connection look like a protocol that is allowed in the network, circumventing firewalls that reject protocols they don't recognize. From edbeb9c6eb06ff4a8e5f735331d3abb2caa4233a Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:40:13 -0400 Subject: [PATCH 4/6] Update Access Key Configuration Reference Add detailed configuration examples and explanations for Outline VPN, including TCP/UDP transport, YAML anchors, and WebSocket support. --- docs/vpn/reference/access-key-config.md | 248 +++++++++++++++++++++++- 1 file changed, 239 insertions(+), 9 deletions(-) diff --git a/docs/vpn/reference/access-key-config.md b/docs/vpn/reference/access-key-config.md index 19889a45..25a46ee7 100644 --- a/docs/vpn/reference/access-key-config.md +++ b/docs/vpn/reference/access-key-config.md @@ -3,7 +3,158 @@ title: "Access Key Configuration Reference" sidebar_label: "Access Key Config" --- -# Config +# Outline Config Reference + +Outline uses a YAML-based configuration to define VPN parameters and handle +TCP/UDP traffic. The configuration supports composability at multiple levels, +enabling flexible and extensible setups. + +The top-level configuration specifies a [TunnelConfig](#tunnelconfig). + +## Examples + +A typical Shadowsocks configuration will look like this: + +```yaml +transport: + $type: tcpudp + + tcp: + $type: shadowsocks + endpoint: ss.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: SECRET + prefix: "POST " + + udp: + $type: shadowsocks + endpoint: ss.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: SECRET +``` + +Note how we can now have TCP and UDP running on different ports or endpoints. + +You can take advantage of YAML anchors and the `<<` merge key to avoid +duplication: + +```yaml +transport: + $type: tcpudp + + tcp: + <<: &shared + $type: shadowsocks + endpoint: ss.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: SECRET + prefix: "POST " + + udp: *shared +``` + +It's now possible to compose strategies and do multi-hops: + +```yaml +transport: + $type: tcpudp + + tcp: + $type: shadowsocks + + endpoint: + $type: dial + address: exit.example.com:4321 + dialer: + $type: shadowsocks + address: entry.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: ENTRY_SECRET + + cipher: chacha20-ietf-poly1305 + secret: EXIT_SECRET + + udp: *shared +``` + +In case of blocking of "look-like-nothing" protocols like Shadowsocks, you +can use Shadowsocks over WebSockets. See the +[server example configuration](https://github.com/Jigsaw-Code/outline-ss-server/blob/master/cmd/outline-ss-server/config_example.yml) +on how to deploy it. A client configuration will look like: + +```yaml +transport: + $type: tcpudp + tcp: + $type: shadowsocks + endpoint: + $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/tcp + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET + + udp: + $type: shadowsocks + endpoint: + $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/udp + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET +``` + +Note that the Websocket endpoint can, in turn, take an endpoint, which can be +used to bypass DNS-based blocking: + +```yaml +transport: + $type: tcpudp + tcp: + $type: shadowsocks + endpoint: + $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/tcp + endpoint: cloudflare.net:443 + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET + + udp: + $type: shadowsocks + endpoint: + $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/udp + endpoint: cloudflare.net:443 + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET +``` + +Note that WebSockets is not yet supported on Windows. In order to have a single +config for all platforms, use a `first-supported` for backwards-compatibility: + +```yaml +transport: + $type: tcpudp + tcp: + $type: shadowsocks + endpoint: + $type: first-supported + options: + - $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/tcp + - ss.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET + + udp: + $type: shadowsocks + endpoint: + $type: first-supported + options: + - $type: websocket + url: wss://legendary-faster-packs-und.trycloudflare.com/SECRET_PATH/udp + - ss.example.com:4321 + cipher: chacha20-ietf-poly1305 + secret: SS_SECRET +``` ## Tunnels @@ -128,18 +279,18 @@ allows for composition of strategies. ### WebsocketEndpointConfig -Tunnels stream and packet connections to an endpoint over Websockets. +Tunnels stream and packet connections to an endpoint over WebSockets. -For stream connections, each write is turned into a Websocket message. For -packet connections, each packet is turned into a Websocket message. +For stream connections, each write is turned into a WebSocket message. For +packet connections, each packet is turned into a WebSocket message. **Format:** _struct_ **Fields:** -- `url` (_string_): the URL for the Websocket endpoint. The schema must be - `https` or `wss` for Websocket over TLS, and `http` or `ws` for plaintext - Websocket. +- `url` (_string_): the URL for the WebSocket endpoint. The schema must be + `https` or `wss` for WebSocket over TLS, and `http` or `ws` for plaintext + WebSocket. - `endpoint` ([EndpointConfig](#endpointconfig)): the web server endpoint to connect to. If absent, is connects to the address specified in the URL. @@ -155,11 +306,17 @@ Packet Dialers. The _null_ (absent) Dialer means the default Dialer, which uses direct TCP connections for Stream and direct UDP connections for Packets. -Supported Interface types for Stream and Packer Dialers: +Supported Interface types for Stream and Packet dialers: - `first-supported`: [FirstSupportedConfig](#firstsupportedconfig) - `shadowsocks`: [ShadowsocksConfig](#shadowsocksconfig) +Supported Interface types for Stream dialers: + +- `iptable`: [IPTableConfig](#iptableconfig) +- `direct`: [Direct](#direct) +- `block`: [Block](#block) + ## Packet Listeners A Packet Listener establishes an unbounded packet connection that can be used to @@ -253,6 +410,79 @@ secret: SECRET prefix: "POST " ``` +### Selective Routing + +#### IPTableConfig + +Represents a Stream Dialer that routes connections based on the +destination IP address. It matches the destination IP against a list of rules +in the `table`. If a rule matches, the connection is handled by the `dialer` +specified in that rule. If no rules match, the connection is handled by the +`fallback` dialer. + +This is a Stream-only dialer and should be used for `tcp` transports. + +**Format:** _struct_ + +**Fields:** + + - `table` (*list*): A list of routing rules. + - `ips` (*list*): A list of IP addresses or CIDR ranges (e.g., `192.0.2.0/24`). + - `dialer` ([DialerConfig](#dialerconfig)): The dialer to use if the destination IP matches an entry in `ips`. + - `fallback` ([DialerConfig](#dialerconfig)): The dialer to use if the + destination IP does not match any rule in the `table`. + +Example: + +```yaml +# This config blocks TCP connections to 192.0.2.0/24 and sends all +# other TCP traffic directly. +transport: + $type: tcpudp + tcp: + $type: iptable + table: + - ips: + - 192.0.2.0/24 + dialer: + $type: block + fallback: + $type: direct + udp: + $type: shadowsocks + # ... udp config +``` + +## Utility Dialers + +### Direct + +Represents a direct Stream dialer. + +This is a Stream-only dialer and should be used for `tcp` transports. + +Example: + +```yaml +dialer: + $type: direct +``` + +### Block + +Represents a dialer that blocks all connection attempts. This can be useful +for explicitly denying traffic to specific destinations, especially when +used with [IPTableConfig](#iptableconfig). + +This is a Stream-only dialer and should be used for `tcp` transports. + +Example: + +```yaml +dialer: + $type: block +``` + ## Meta Definitions ### FirstSupportedConfig @@ -290,4 +520,4 @@ $type: shadowsocks endpoint: example.com:4321 cipher: chacha20-ietf-poly1305 secret: SECRET -``` \ No newline at end of file +``` From 4b62292e314a582f2b25f9258a58c0361837de6a Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:29:33 -0400 Subject: [PATCH 5/6] Update link for Dynamic Access Keys documentation --- docs/vpn/advanced/prefixing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/vpn/advanced/prefixing.md b/docs/vpn/advanced/prefixing.md index ed475fd9..27a47adb 100644 --- a/docs/vpn/advanced/prefixing.md +++ b/docs/vpn/advanced/prefixing.md @@ -49,7 +49,7 @@ Some examples of effective UDP prefixes that look like common protocols: ### Dynamic Access Keys -To use the prefix feature with [Dynamic Access Keys](/outline/docs/guides/service-providers/dynamic-access-keys.md) (`ssconf://`), +To use the prefix feature with [Dynamic Access Keys](../management/dynamic-access-keys.md) (`ssconf://`), add a "prefix" key to the YAML object, with a **YAML-encoded** value representing the prefix you want_ (see examples in the table above)_. You can use escape codes (like \u00FF) to represent non-printable Unicode codepoints in From e3fb21ea13b78e82d6c8b81704b85eab4100d1ac Mon Sep 17 00:00:00 2001 From: "J. Yi" <93548144+jyyi1@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:31:53 -0400 Subject: [PATCH 6/6] Fix capitalization of 'Websockets' in access key config Corrected capitalization of 'Websockets' and 'Websocket' throughout the document for consistency. --- docs/vpn/reference/access-key-config.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/vpn/reference/access-key-config.md b/docs/vpn/reference/access-key-config.md index 25a46ee7..0ec2d1b3 100644 --- a/docs/vpn/reference/access-key-config.md +++ b/docs/vpn/reference/access-key-config.md @@ -3,7 +3,7 @@ title: "Access Key Configuration Reference" sidebar_label: "Access Key Config" --- -# Outline Config Reference +# Config Outline uses a YAML-based configuration to define VPN parameters and handle TCP/UDP traffic. The configuration supports composability at multiple levels, @@ -78,7 +78,7 @@ transport: ``` In case of blocking of "look-like-nothing" protocols like Shadowsocks, you -can use Shadowsocks over WebSockets. See the +can use Shadowsocks over Websockets. See the [server example configuration](https://github.com/Jigsaw-Code/outline-ss-server/blob/master/cmd/outline-ss-server/config_example.yml) on how to deploy it. A client configuration will look like: @@ -127,7 +127,7 @@ transport: secret: SS_SECRET ``` -Note that WebSockets is not yet supported on Windows. In order to have a single +Note that Websockets is not yet supported on Windows. In order to have a single config for all platforms, use a `first-supported` for backwards-compatibility: ```yaml @@ -279,18 +279,18 @@ allows for composition of strategies. ### WebsocketEndpointConfig -Tunnels stream and packet connections to an endpoint over WebSockets. +Tunnels stream and packet connections to an endpoint over Websockets. -For stream connections, each write is turned into a WebSocket message. For -packet connections, each packet is turned into a WebSocket message. +For stream connections, each write is turned into a Websocket message. For +packet connections, each packet is turned into a Websocket message. **Format:** _struct_ **Fields:** -- `url` (_string_): the URL for the WebSocket endpoint. The schema must be - `https` or `wss` for WebSocket over TLS, and `http` or `ws` for plaintext - WebSocket. +- `url` (_string_): the URL for the Websocket endpoint. The schema must be + `https` or `wss` for Websocket over TLS, and `http` or `ws` for plaintext + Websocket. - `endpoint` ([EndpointConfig](#endpointconfig)): the web server endpoint to connect to. If absent, is connects to the address specified in the URL.