Skip to content

Add Shadowsocks over WebSocket (SS over WSS) Support #1676

@lunarthegrey

Description

@lunarthegrey

Feature Request: Add Shadowsocks over WebSocket (SS over WSS) Support

Fork where changes were made: https://github.com/unredacted/outline-server

Summary

Add support for tunneling Shadowsocks traffic over WebSocket connections to enhance censorship resistance and improve connectivity in restrictive network environments.

Motivation

Current Limitations

The current Outline Server implementation only supports direct Shadowsocks connections, which can be problematic in several scenarios:

  1. Deep Packet Inspection (DPI): Modern censorship systems can detect and block Shadowsocks traffic patterns
  2. Corporate Firewalls: Many enterprise networks only allow HTTP/HTTPS traffic through proxies
  3. Network Restrictions: Some networks block non-standard ports or protocols
  4. Protocol Fingerprinting: Direct Shadowsocks connections have identifiable characteristics

Proposed Solution

I've implemented a complete solution that adds WebSocket transport support while maintaining backward compatibility. The implementation allows servers to support both traditional Shadowsocks connections and WebSocket-tunneled connections simultaneously.

Key Features

  1. Flexible Configuration: Each access key can optionally enable WebSocket transport
  2. Dual Protocol Support: Separate paths for TCP and UDP over WebSocket
  3. Dynamic Client Configuration: Automatically generates appropriate client configs
  4. Backward Compatible: Servers without WebSocket keys continue using the legacy format
  5. Mixed Mode Operation: Supports both WebSocket and traditional clients simultaneously

API Changes

New WebSocket configuration options for access keys:

{
  "name": "User with WebSocket",
  "websocket": {
    "enabled": true,
    "tcpPath": "/tcp",
    "udpPath": "/udp", 
    "domain": "example.com",
    "tls": true
  }
}

Configuration Format

The implementation automatically generates the appropriate outline-ss-server configuration:

When WebSocket keys exist:

web:
  servers:
    - id: outline-ws-server
      listen:
        - '127.0.0.1:8080'
services:
  - listeners:
      - type: tcp
        address: '[::]:443'
      - type: udp
        address: '[::]:443'
    keys:
      - id: '0'
        cipher: chacha20-ietf-poly1305
        secret: <secret>
  - listeners:
      - type: websocket-stream
        web_server: outline-ws-server
        path: /tcp
      - type: websocket-packet
        web_server: outline-ws-server
        path: /udp
    keys:
      - id: '1'
        cipher: chacha20-ietf-poly1305
        secret: <secret>

Client Configuration

WebSocket-enabled keys return dynamic YAML configuration:

transport:
  $type: tcpudp
  tcp:
    $type: shadowsocks
    endpoint:
      $type: websocket
      url: 'wss://example.com/tcp'
    cipher: chacha20-ietf-poly1305
    secret: <secret>
  udp:
    $type: shadowsocks
    endpoint:
      $type: websocket
      url: 'wss://example.com/udp'
    cipher: chacha20-ietf-poly1305
    secret: <secret>

Implementation Details

Components Modified

  1. API Schema (api.yml): Added WebSocketConfig schema
  2. Data Model (access_key.ts): Extended interfaces for WebSocket support
  3. Manager Service (manager_service.ts): Added validation and dynamic config generation
  4. Access Key Repository (server_access_key.ts): Stores WebSocket configuration
  5. Shadowsocks Server (outline_shadowsocks_server.ts): Generates WebSocket-aware configs
  6. Dependencies: Updated to outline-ss-server v1.9.2 with WebSocket support

Reverse Proxy Setup

The WebSocket server runs on a separate port (8080) and requires reverse proxy configuration with nginx example below:

location /tcp {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

location /udp {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

This can also be fronted by using something like Cloudflare Tunnel.

Testing

The implementation has been thoroughly tested with:

  • Creating WebSocket-enabled access keys via API
  • Generating proper server configurations for mixed environments
  • Client connections using dynamic access key configurations
  • Simultaneous traditional and WebSocket client connections
  • Multi-platform Docker image builds (amd64/arm64)

Future Enhancements

  1. Configurable WebSocket Port: Currently hardcoded to 8080
  2. Custom Headers: Support for additional WebSocket headers
  3. Path Randomization: Generate random paths for enhanced security

Contribution

I have a working implementation in my fork that I'm ready to contribute. The code:

  • Maintains backward compatibility
  • Follows existing code patterns and conventions
  • Includes proper error handling and validation
  • Has been tested in production environments

Would the team be interested in reviewing a pull request for this feature? I'm happy to make any adjustments to align with the project's goals and standards.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiRelated to the REST APIkey/managementissues related to managing access keysneeds more infoWe need more information in order to help or verifyserver

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions