Shadowsocks is a versatile open-source proxy tool designed to bypass internet censorship and provide users with secure and private access to the internet. It works by creating an encrypted tunnel between the user’s device and a remote server, allowing them to circumvent firewalls and access blocked content. With its lightweight and efficient design, Shadowsocks has become a popular choice for individuals seeking to maintain anonymity and freedom while browsing the web, particularly in regions with strict online censorship policies.

Shadowsocks differs from traditional Virtual Private Networks (VPNs) in several key ways. While both technologies aim to provide users with secure and private access to the internet, they employ different methods to achieve this goal.

Shadowsocks can indeed be utilized to encrypt all traffic passing through a device, providing a quick and efficient means of maintaining encryption, even on unsecured or unsafe WiFi connections. By encrypting all traffic, Shadowsocks helps to safeguard sensitive information and protect user privacy, regardless of the network environment. This can be particularly valuable when connecting to public WiFi networks in cafes, airports, or other shared spaces where security may be a concern. In such scenarios, Shadowsocks offers a convenient and effective solution for ensuring that selective data transmitted over the network remains secure and private.

However, differently than VPNs with full-redirection of traffic, clients are responsible to configure which traffic is going to be forwarded to the local proxy to ensure it is encrypted and forwarded through the proxy server.

Shadowsocks works over TCP/UDP at the application level of the ISO/OSI stack and this is visible up to the clients applications (for example, browsers). VPNs also work on TCP/UDP connection at an application level for the actual client-server communication, but the client OS masks this communication by leveraging the network stack in the Kernel: usually, a virtual network interface is created and the route table is modified such that all the traffic is sent through the gateway reachable through the virtual network interface, except the actual communication with the VPN server.

Shadowsocks Clients run another server locally to encrypt any traffic directed through them and forward it in the other side to the Shadowsocks Server that will de-encapsulate the request, forward to the final destination and then back to the client, re-encrypted.

graph LR Client --> ShadowSocksClient --> Internet --> ShadowSocksServer --> WebSite

So far, this has been the quickest way for me to hack the great firewall and access censored websites while in China.

Fully-encrypted VPNs are blocked very quickly thanks to the anomaly detection systems the Chinese telecommunication companies run in their networks.

Shadowsocks servers could be banned too: I got a server in a Japan data-center banned after a few hours. Another, though, is still active and reachable after months of activity.

Deploying the Shadowsocks server on Microshift

Microshift is a project by Red Hat that optimizes OpenShift, a container orchestration platform based on Kubernetes, for small form-factor devices and edge computing environments.

Microshift focuses on Resource Efficiency, providing a very reduced set of APIs compared to the Openshift ones and is based on almost null set of cluster operators. I use to deploy it on my cheap virtual private servers through a dedicated Fedora CoreOS container image leveraging layering.

In the context of deploying a Shadowsocks server, we can consider it just as another Kubernetes distribution that we can run on our servers.

Here are the minimum manifests I managed to use for deploying this solution on my deployment:

RBAC

Like Openshift, Microshift implements SecurityContextConstraints (SCC) to enforce the capabilities of pods. As Shadowsocks servers require specific Linux capabilities, we need to allow the service account running it to use the privileged SCC:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: scc-privileged-shadowsocks
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:openshift:scc:privileged
subjects:
  - kind: ServiceAccount
    name: default
    namespace: shadowsocks
Configuration secret

The configuration of Shadowsocks is well-described in their documentation. We can use a secret to store the configuration and mount it later in the deployment object:

kind: Secret
apiVersion: v1
metadata:
  name: shadowsocks-conf
stringData:
  config.json: |
    {
      "server": [ "::", "0.0.0.0" ],
      "mode": "tcp_and_udp",
      "server_port": 8388,
      "local_port": 1080,
      "password": "CHANGE-ME---CHANGE-ME",
      "timeout": 86400,
      "method": "xchacha20-ietf-poly1305"
    }    

As you can see, shadowsocks can work on both UDP and TCP.

Service

We can rely on NodePort services to expose shadowsocks.

kind: Service
apiVersion: v1
metadata:
  name: shadowsocks
spec:
  type: NodePort
  selector:
    app: shadowsocks
  ports:
    - port: 8388
      targetPort: 8388
      nodePort: 32000
      protocol: TCP
    - port: 8388
      targetPort: 8388
      nodePort: 32000
      protocol: UDP

Be careful on dedicating a proper NodePort that is not already bound and statically such that it is easier for you to compute the endpoint for the client

Deployment
kind: Deployment
apiVersion: apps/v1
metadata:
  name: shadowsocks
spec:
  selector:
    matchLabels:
      app: shadowsocks
  template:
    metadata:
      labels:
        app: shadowsocks
    spec:
      containers:
        - name: shadowsocks
          securityContext:
            runAsUser: 999
            runAsNonRoot: true
            allowPrivilegeEscalation: false
            capabilities:
              add:
                - NET_RAW
          image: docker.io/shadowsocks/shadowsocks-libev:v3.3.5
          command:
            - /usr/bin/ss-server
            - -c
          args:
            - /etc/shadowsocks/config.json
          volumeMounts:
            - mountPath: /etc/shadowsocks
              name: shadowsocks-conf
          readinessProbe:
            tcpSocket:
              port: 8388
          livenessProbe:
            tcpSocket:
              port: 8388
      volumes:
        - name: shadowsocks-conf
          secret:
            secretName: shadowsocks-conf
            defaultMode: 0444

Clients

Mobile

There are mobile applications available to connect to the web through the Shadowsocks server. You will mostly have to resemble the same configuration as in the config.json stored in the secret and refer to the IP of the server for the connection.

It’s worth noting it that chinese-specific applications like WeChat and AliPay may detect you on another country, leading some services like payment services to fail. Be sure of excluding these applications in the Shadowsocks client app configuration.

Linux Desktops

You can run the client via Podman/Docker using a similar configuration file to the one for the server:

    {
      "server": [ "SERVER_IP" ],
      "mode": "tcp_and_udp",
      "server_port": 32000, 
      "local_port": 1080,
      "password": "CHANGE-ME---CHANGE-ME",
      "timeout": 86400,
      "method": "xchacha20-ietf-poly1305"
    }

server_port is set to the NodePort chosen as in the previous section.

For example, assuming the json file is stored at /home/user/shadowsocks.json, you can run

podman run -p 1080:1080 -v /home/user/shadowsocks.json:/etc/shadowsocks.json:Z -it \
  docker.io/shadowsocks/shadowsocks-libev:v3.3.5 /usr/bin/ss-local -c /etc/shadowsocks.json

As anticipated, you will have to specify each application that you want to go through the proxy:

http_proxy=socks5h://localhost:1080 https_proxy=socks5h://localhost:1080 curl https://ifconfig.me

You can configure your browser, or any other applications to forward traffic through the local proxy.

Here in the following, a systemd unit that I use to run the server locally:

[Unit]
Description=Runs the Shadowsocks client
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers
AssertPathExists=/etc/shadowsocks.json

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
ExecStartPre=/usr/bin/podman rm -f --ignore shadowsocks-client
ExecStart=/usr/bin/podman run \
            -p 127.0.0.1:1080:1080/tcp
            --name shadowsocks-client -v /etc/shadowsocks.json:/etc/shadowsocks.json:Z \
            docker.io/shadowsocks/shadowsocks-libev:v3.3.5 \
            /usr/bin/ss-local -c /etc/shadowsocks.json
ExecStop=/usr/bin/podman stop --ignore shadowsocks-client
ExecStopPost=/usr/bin/podman rm -f --ignore shadowsocks-client
Restart=always
RestartSec=5
Type=simple

[Install]
WantedBy=default.target

Linux users will be familiar on how to configure their environment to forward the traffic properly.