├── .gitattributes ├── .gitmodules ├── src ├── docker-healthcheck.sh ├── config │ └── danted.conf └── docker-entrypoint.sh ├── .dockerignore ├── deploy-container.sh ├── LICENSE ├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── docker-compose.yml ├── .gitignore ├── Dockerfile └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # automatically declare all files as text and use LF as line end-of-line 2 | * text eol=lf 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/dotenv"] 2 | path = lib/dotenv 3 | url = https://github.com/bashup/dotenv.git 4 | -------------------------------------------------------------------------------- /src/docker-healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # check windscribe status 3 | if windscribe status | grep -q "DISCONNECTED"; then 4 | exit 1 5 | fi 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ### Dockerignore whitelist ### 2 | 3 | # ignore all files by default 4 | * 5 | 6 | # whitelist sources 7 | !src/ 8 | !lib/dotenv/dotenv 9 | -------------------------------------------------------------------------------- /src/config/danted.conf: -------------------------------------------------------------------------------- 1 | # logging 2 | debug: 0 3 | logoutput: stderr 4 | 5 | # networking interfaces 6 | internal: 0.0.0.0 port = 1080 7 | external: tun0 8 | 9 | # users 10 | user.privileged: root 11 | user.unprivileged: nobody 12 | 13 | # client authentication mechanism; possible values are 'username' and 'none' 14 | socksmethod: ${SOCKS_METHOD} 15 | 16 | 17 | # routing 18 | 19 | client pass { 20 | from: 0.0.0.0/0 to: 0.0.0.0/0 21 | log: connect disconnect error 22 | } 23 | 24 | socks pass { 25 | from: 0.0.0.0/0 to: 0.0.0.0/0 26 | log: connect disconnect error 27 | } 28 | -------------------------------------------------------------------------------- /deploy-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # explicitly define variables here or source them from an .env file with --env-file flag 3 | #export WINDSCRIBE_DNS="1.1.1.1" 4 | #export WINDSCRIBE_USERNAME="username" 5 | #export WINDSCRIBE_PASSWORD="password" 6 | #export WINDSCRIBE_LOCATION="" 7 | 8 | docker run \ 9 | --detach \ 10 | --restart=always \ 11 | --cap-add=NET_ADMIN \ 12 | --publish 1080:1080 \ 13 | --tmpfs /etc/windscribe:exec \ 14 | --env WINDSCRIBE_DNS \ 15 | --env WINDSCRIBE_USERNAME \ 16 | --env WINDSCRIBE_PASSWORD \ 17 | --env WINDSCRIBE_LOCATION \ 18 | --env SOCKS_USERNAME \ 19 | --env SOCKS_PASSWORD \ 20 | --env-file .env \ 21 | --name windscribe \ 22 | "concisions/windscribe-socks-server:latest" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Concision 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Continuous Integration" 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | name: "Build Image" 9 | runs-on: ubuntu-18.04 10 | steps: 11 | - name: "Checkout Git Repository" 12 | uses: actions/checkout@v2 13 | with: 14 | # clone Git submodules 15 | submodules: true 16 | 17 | - name: "Docker: Set up QEMU" 18 | uses: docker/setup-qemu-action@v1 19 | 20 | - name: "Docker: Set up Buildx" 21 | uses: docker/setup-buildx-action@v1 22 | 23 | - name: "Cache: Docker layers" 24 | uses: actions/cache@v2 25 | with: 26 | path: "/tmp/.buildx-cache" 27 | key: ${{ runner.os }}-buildx-${{ github.sha }} 28 | restore-keys: | 29 | ${{ runner.os }}-buildx- 30 | 31 | - name: "Docker: Build" 32 | uses: docker/build-push-action@v2 33 | with: 34 | context: . 35 | file: ./Dockerfile 36 | platforms: linux/386,linux/amd64,linux/arm/v7 37 | tags: ghcr.io/concisions/windscribe-socks-server:latest 38 | cache-from: type=local,src=/tmp/.buildx-cache 39 | cache-to: type=local,dest=/tmp/.buildx-cache 40 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | windscribe: 5 | image: "concisions/windscribe-socks-server:latest" 6 | # restart if stopped (see https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy) 7 | restart: always 8 | # Windscribe utilizes iptables and requires additional permissions (see https://stackoverflow.com/a/44523905) 9 | # However, there are security ramifications to this (see CAP_NET_ADMIN at http://man7.org/linux/man-pages/man7/capabilities.7.html) 10 | cap_add: 11 | - NET_ADMIN 12 | # publicly expose SOCKS5 server 13 | ports: 14 | - 1080:1080 15 | # mount temporary container storage (see https://docs.docker.com/storage/tmpfs/) 16 | tmpfs: 17 | - /etc/windscribe:exec 18 | # specify configuration through environment variables 19 | environment: 20 | # set DNS servers; whitespace delimited (default: 1.1.1.1; see https://1.1.1.1/dns/) 21 | WINDSCRIBE_DNS: "${WINDSCRIBE_DNS:-1.1.1.1}" 22 | # Windscribe configuration; variable may be modified here or be sourced from '.env' file 23 | WINDSCRIBE_USERNAME: "${WINDSCRIBE_USERNAME:?A Windscribe account username must be specified}" 24 | WINDSCRIBE_PASSWORD: "${WINDSCRIBE_PASSWORD:?A Windscribe account password must be specified}" 25 | # requests a specific location 26 | WINDSCRIBE_LOCATION: "${WINDSCRIBE_LOCATION}" 27 | # socks user 1 (if any SOCKS_USERNAME_XYZ is specified, authentication is enabled) 28 | SOCKS_USERNAME_1: "${SOCKS_USERNAME:-}" 29 | SOCKS_PASSWORD_1: "${SOCKS_PASSWORD:-}" 30 | # socks user 2 31 | SOCKS_USERNAME_2: "" 32 | SOCKS_PASSWORD_2: "" 33 | # or comment out the above section and specify configuration through a secrets file in .env format 34 | # secrets: 35 | # - windscribe_server 36 | #secrets: 37 | # windscribe_server: 38 | # file: secrets.env 39 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Continuous Delivery" 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | name: "Build & Push Image" 11 | runs-on: ubuntu-18.04 12 | steps: 13 | - name: "Checkout Git Repository" 14 | uses: actions/checkout@v2 15 | with: 16 | # clone Git submodules 17 | submodules: true 18 | 19 | - name: "Docker: Generate Tags" 20 | id: docker_meta 21 | uses: crazy-max/ghaction-docker-meta@v1 22 | with: 23 | images: | 24 | ghcr.io/concision/docker-windscribe-socks-server/windscribe-socks-server 25 | concisions/windscribe-socks-server 26 | tag-semver: | 27 | {{version}} 28 | {{major}}.{{minor}} 29 | 30 | - name: "Docker: Set up QEMU" 31 | uses: docker/setup-qemu-action@v1 32 | 33 | - name: "Docker: Set up Buildx" 34 | uses: docker/setup-buildx-action@v1 35 | 36 | - name: "DockerHub: Login" 37 | uses: docker/login-action@v1 38 | with: 39 | username: ${{ secrets.DOCKERHUB_USERNAME }} 40 | password: ${{ secrets.DOCKERHUB_TOKEN }} 41 | 42 | - name: "GitHub Container Registry: Login" 43 | uses: docker/login-action@v1 44 | with: 45 | registry: ghcr.io 46 | username: ${{ github.repository_owner }} 47 | password: ${{ secrets.GHCR_PAT }} 48 | 49 | - name: "Docker: Build and Push" 50 | uses: docker/build-push-action@v2 51 | with: 52 | context: . 53 | file: ./Dockerfile 54 | platforms: linux/386,linux/amd64,linux/arm/v7 55 | push: true 56 | tags: ${{ steps.docker_meta.outputs.tags }} 57 | cache-from: type=registry,ref=ghcr.io/concision/docker:cache 58 | cache-to: type=registry,ref=ghcr.io/concision/docker:cache 59 | 60 | - name: "DockerHub: Update README" 61 | uses: peter-evans/dockerhub-description@v2 62 | with: 63 | username: ${{ secrets.DOCKERHUB_USERNAME }} 64 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 65 | repository: concisions/windscribe-socks-server 66 | readme-filepath: ./README.md 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Project Specific ### 2 | .env 3 | 4 | 5 | # Created by https://www.gitignore.io/api/intellij+all 6 | # Edit at https://www.gitignore.io/?templates=intellij+all 7 | 8 | ### Intellij+all ### 9 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 10 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 11 | 12 | # User-specific stuff 13 | .idea/**/workspace.xml 14 | .idea/**/tasks.xml 15 | .idea/**/usage.statistics.xml 16 | .idea/**/dictionaries 17 | .idea/**/shelf 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/modules.xml 40 | # .idea/*.iml 41 | # .idea/modules 42 | # *.iml 43 | # *.ipr 44 | 45 | # CMake 46 | cmake-build-*/ 47 | 48 | # Mongo Explorer plugin 49 | .idea/**/mongoSettings.xml 50 | 51 | # File-based project format 52 | *.iws 53 | 54 | # IntelliJ 55 | out/ 56 | 57 | # mpeltonen/sbt-idea plugin 58 | .idea_modules/ 59 | 60 | # JIRA plugin 61 | atlassian-ide-plugin.xml 62 | 63 | # Cursive Clojure plugin 64 | .idea/replstate.xml 65 | 66 | # Crashlytics plugin (for Android Studio and IntelliJ) 67 | com_crashlytics_export_strings.xml 68 | crashlytics.properties 69 | crashlytics-build.properties 70 | fabric.properties 71 | 72 | # Editor-based Rest Client 73 | .idea/httpRequests 74 | 75 | # Android studio 3.1+ serialized cache file 76 | .idea/caches/build_file_checksums.ser 77 | 78 | ### Intellij+all Patch ### 79 | # Ignores the whole .idea folder and all .iml files 80 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 81 | 82 | .idea/ 83 | 84 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 85 | 86 | *.iml 87 | modules.xml 88 | .idea/misc.xml 89 | *.ipr 90 | 91 | # Sonarlint plugin 92 | .idea/sonarlint 93 | 94 | # End of https://www.gitignore.io/api/intellij+all 95 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ### Ubuntu 18 2 | # Same version as built image to minimize pulls 3 | FROM ubuntu:bionic-20200921 AS sources 4 | 5 | ## Linux Depenedencies 6 | # install dos2unix to sanitize dotenv CRLF line endings 7 | RUN apt-get update && apt-get install -y dos2unix 8 | 9 | ## Project Sources 10 | # copy scripts 11 | COPY src/docker-entrypoint.sh src/docker-healthcheck.sh lib/dotenv/dotenv /wss/home/wss/ 12 | # ensure scripts are executable 13 | RUN dos2unix /wss/home/wss/* && chmod +x /wss/home/wss/* 14 | 15 | # copy Danted Configuration 16 | COPY src/config/danted.conf /wss/etc/danted.conf 17 | 18 | 19 | ### Ubuntu 18 20 | # Using ubuntu 18, as there appears to be compatiblity issues with later ubuntu versions on linux/arm/v7. 21 | # See https://bugs.launchpad.net/cloud-images/+bug/1896443 22 | FROM ubuntu:bionic-20200921 23 | 24 | ## Linux Dependencies 25 | # install Windscribe and Dante server 26 | RUN \ 27 | # update package listings 28 | apt-get update && \ 29 | # install dependencies 30 | apt-get install -y \ 31 | # apt-key 32 | gnupg2 \ 33 | # verify Windscribe repository 34 | ca-certificates \ 35 | # fix Windscribe's resolveconf linux dependency 36 | apt-utils debconf-utils dialog \ 37 | # required for Windscribe 38 | iptables \ 39 | # IP healthcheck 40 | curl \ 41 | # danted proxy server 42 | dante-server && \ 43 | # fix resolveconf dependency configuration (as per https://stackoverflow.com/a/51507868) 44 | echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \ 45 | echo "resolvconf resolvconf/linkify-resolvconf boolean false" | debconf-set-selections && \ 46 | # add Windscribe signing key 47 | apt-key adv --keyserver keyserver.ubuntu.com --recv-key FDC247B7 && \ 48 | # add Windscribe repository 49 | echo 'deb https://repo.windscribe.com/ubuntu bionic main' | tee /etc/apt/sources.list.d/windscribe-repo.list && \ 50 | # update repository 51 | apt-get update && \ 52 | # install Windscribe 53 | apt-get install -y windscribe-cli && \ 54 | # remove Windscribe repository key 55 | apt-key del FDC247B7 && \ 56 | # remove packages 57 | apt-get purge -y gnupg2 apt-utils debconf-utils apt-utils debconf-utils dialog && apt-get autoremove -y && \ 58 | # remove Windscribe repository file 59 | rm -rf /etc/apt/sources.list.d/windscribe-repo.list && \ 60 | # cleanup apt-get lists 61 | apt-get clean && \ 62 | rm -rf /var/lib/apt/lists/* && \ 63 | # clear logs 64 | rm -rf /var/log/* 65 | 66 | ### Project Layer 67 | COPY --from=sources /wss / 68 | 69 | ## Metadata 70 | # maintainer tag 71 | LABEL maintainer="contact@concision.me" 72 | 73 | ## Configure Image 74 | # expose SOCKS5 server port 75 | EXPOSE 1080/tcp 76 | # default entrypoint command 77 | CMD ["/home/wss/docker-entrypoint.sh"] 78 | # default docker healthcheck 79 | HEALTHCHECK --interval=120s --timeout=30s --start-period=15s --retries=3 \ 80 | CMD "/home/wss/docker-healthcheck.sh" 81 | -------------------------------------------------------------------------------- /src/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ### Formatting 6 | # command prefixing (source: https://unix.stackexchange.com/a/440514) 7 | prefixWith() { 8 | local prefix="$1" 9 | shift 10 | 11 | # redirect standard input and error to different commands (source: https://stackoverflow.com/a/31151808) 12 | { "$@" 2>&1 1>&3 3>&- | { while read -r line; do echo "$prefix $line"; done; }; } 3>&1 1>&2 | { while read -r line; do echo "$prefix $line"; done; } 13 | } 14 | 15 | ### Execute script with timestamp logging 16 | { { 17 | # scope Docker secret variables to be wiped after initialization 18 | ( 19 | ### Load Secrets 20 | if [[ -d "/run/secrets/" ]]; then 21 | if [[ -f "/run/secrets/windscribe_server" ]]; then 22 | # load dotenv bash library 23 | source "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )/dotenv" 24 | 25 | prefixWith "[DOCKER]" echo "Loading secrets from 'windscribe_server'" 26 | .env --file "/run/secrets/windscribe_server" export || { 27 | prefixWith "[DOTENV]" echo ".env secret file is invalidly formatted and failed to load." 1>&2 28 | exit 1 29 | } 30 | else 31 | prefixWith "[DOCKER]" echo "Secrets must be specified with the name 'windscribe_server' (detected secrets: $(ls /run/secrets))" 1>&2 32 | fi 33 | else 34 | prefixWith "[DOCKER]" echo "No configured secrets detected; secrets can be specified with an .env file under secret name 'windscribe_server'" 35 | fi 36 | 37 | 38 | ### Sanity Checks 39 | # Ensure shell is non-interactive 40 | if [ -t 0 ] ; then 41 | echo "Container cannot be run interactively" 1>&2 42 | exit 1 43 | fi 44 | 45 | # iptable support checks 46 | iptables -vnL > /dev/null 2>&1 || { 47 | prefixWith "[IPTABLES]" echo "Ensure cap_add is set to NET_ADMIN" 1>&2 48 | exit 1 49 | } 50 | 51 | # username checks 52 | if [[ -z "${WINDSCRIBE_USERNAME}" ]]; then 53 | prefixWith "[WINDSCRIBE]" echo "Unset Windscribe username; ensure that the variable \$WINDSCRIBE_USERNAME is set properly" 1>&2 54 | exit 1 55 | fi 56 | if [[ "${WINDSCRIBE_USERNAME}" =~ [^a-zA-Z0-9_] ]]; then 57 | prefixWith "[WINDSCRIBE]" echo "Windscribe username must be alphanumeric (underscores allowed); ensure that the variable \$WINDSCRIBE_USERNAME is set properly" 1>&2 58 | exit 1 59 | fi 60 | # validate password checks 61 | if [[ -z "${WINDSCRIBE_PASSWORD}" ]]; then 62 | prefixWith "[WINDSCRIBE]" echo "Unset Windscribe password; ensure that the variable \$WINDSCRIBE_PASSWORD is set properly" 1>&2 63 | exit 1 64 | fi 65 | # ensure no newlines present (source: https://unix.stackexchange.com/a/276836) 66 | NL=' 67 | ' 68 | case "${WINDSCRIBE_PASSWORD}" in *"${NL}"*) prefixWith "[WINDSCRIBE]" echo "Windscribe password cannot contain new lines; ensure that the variable \$WINDSCRIBE_PASSWORD is set properly" 1>&2 69 | exit 1;; 70 | esac 71 | # validate windscribe location 72 | # ensure no newlines present 73 | case "${WINDSCRIBE_LOCATION}" in *"${NL}"*) prefixWith "[WINDSCRIBE]" echo "Windscribe location cannot contain new lines; ensure that the variable \$WINDSCRIBE_LOCATION is set properly" 1>&2 74 | exit 1;; 75 | esac 76 | 77 | 78 | ### Prepare Dante Accounts 79 | # indicates that proxy server authentication is enabled 80 | authentication=false 81 | prefixWith "[SOCKS5]" echo "Searching for SOCKS_USERNAME and SOCKS_PASSWORD variables" 82 | # search and register proxy server accounts 83 | for socks_username in $(printenv | grep "^SOCKS_USERNAME"); do 84 | # strip SOCKS_USERNAME prefix 85 | VAR=$(echo ${socks_username} | sed --expression='s/^SOCKS_USERNAME//') 86 | # read "SOCKS_USERNAME$suffix=$username" format 87 | IFS='=' read -r suffix username <<< "${VAR}" 88 | 89 | # build variable as string 90 | password_env="SOCKS_PASSWORD$suffix" 91 | # dereference variable 92 | password="${!password_env}" 93 | 94 | # validate username and password 95 | if [[ "${username}" ]]; then 96 | if [[ "${username}" =~ [^a-zA-Z0-9_] ]]; then 97 | prefixWith "[SOCKS5]" echo "Detected user variable from \$SOCKS_USERNAME${suffix}, but the value was not alphanumeric (with _); username: ${username}" 1>&2 98 | exit 1 99 | fi 100 | if [[ "${password}" ]]; then 101 | prefixWith "[SOCKS5]" echo "Detected user variable from \$SOCKS_USERNAME${suffix} (username: ${username}); creating account" 102 | else 103 | prefixWith "[SOCKS5]" echo "Detected user variable from \$SOCKS_USERNAME${suffix}, but no password was specified at \$SOCKS_PASSWORD${suffix}; username: ${username}" 1>&2 104 | exit 1 105 | fi 106 | else 107 | prefixWith "[SOCKS5]" echo "Detected user variable from \$SOCKS_USERNAME${suffix}, but the value was empty; ignoring entry" 1>&2 108 | continue 109 | fi 110 | 111 | # ensure no duplicate account names 112 | if id "${username}" &>/dev/null; then 113 | prefixWith "[SOCKS5]" echo "The requested username (from \$SOCKS_USERNAME${suffix}) is either reserved or already allocated, ignoring; username: ${username}" 1>&2 114 | continue 115 | fi 116 | 117 | # create user account 118 | useradd -s /sbin/nologin "${username}" 119 | yes "${password}" | passwd "${username}" 2>/dev/null 120 | 121 | # mark authentication as enabled 122 | authentication=true 123 | done 124 | if [[ "${authentication}" == false ]]; then 125 | prefixWith "[SOCKS5]" echo "No non-empty SOCKS5 accounts were received (check that the variables are set correctly)" 126 | fi 127 | 128 | 129 | ### Create TUN device for Windscribe 130 | # create TUN device 131 | prefixWith "[OPENVPN]" echo "Creating OpenVPN TUN device" 132 | prefixWith "[OPENVPN]" mkdir -p /dev/net 133 | prefixWith "[OPENVPN]" mknod /dev/net/tun c 10 200 134 | prefixWith "[OPENVPN]" chmod 600 /dev/net/tun 135 | 136 | 137 | ### Start Windscribe client 138 | # define DNS nameservers 139 | prefixWith "[RESOLV]" echo "Writing /etc/resolv.conf" 140 | sed -e 's/\s\+/\n/g;s/\(^\|\n\)/\1nameserver /g' <<< "${WINDSCRIBE_DNS:-1.1.1.1}" > "/etc/resolv.conf" 141 | prefixWith "[RESOLV] config: " cat /etc/resolv.conf 142 | # start windscribe daemon 143 | prefixWith "[WINDSCRIBE]" echo "Starting Windscribe client" 144 | prefixWith "[WINDSCRIBE]" windscribe start 145 | # authenticate to Windscribe 146 | prefixWith "[WINDSCRIBE]" echo "Authenticating to Windscribe" 147 | prefixWith "[WINDSCRIBE]" windscribe login <<- EOF 148 | ${WINDSCRIBE_USERNAME} 149 | ${WINDSCRIBE_PASSWORD} 150 | EOF 151 | prefixWith "[WINDSCRIBE]" windscribe account 152 | # connect to Windscribe 153 | prefixWith "[WINDSCRIBE]" echo "Connecting to Windscribe" 154 | if [[ -n "${WINDSCRIBE_LOCATION}" ]]; then 155 | prefixWith "[WINDSCRIBE]" echo "Windscribe location: ${WINDSCRIBE_LOCATION}" 156 | else 157 | prefixWith "[WINDSCRIBE]" echo "No \$WINDSCRIBE_LOCATION was specified; available Windscribe locations:" 158 | windscribe locations 159 | fi 160 | prefixWith "[WINDSCRIBE]" windscribe connect "${WINDSCRIBE_LOCATION}" 161 | # prevent using untunneled internet 162 | prefixWith "[WINDSCRIBE]" echo "Enabling Windscribe firewall" 163 | prefixWith "[WINDSCRIBE]" windscribe firewall on 164 | 165 | 166 | ### Fix eth0 networking 167 | # get eth0 interface IP (see https://unix.stackexchange.com/a/8521) 168 | INTERFACE_IP=$(ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) 169 | # get eth0 default gateway (see https://stackoverflow.com/a/1226395) 170 | GATEWAY_IP=$(ip route | awk '/default/ { print $3 }') 171 | 172 | # reply to packets on same interface as received (see https://unix.stackexchange.com/a/23345) 173 | echo 200 isp2 >> /etc/iproute2/rt_tables 174 | ip rule add from "${INTERFACE_IP}" table isp2 175 | ip route add default via "${GATEWAY_IP}" table isp2 176 | 177 | 178 | ### Binds SOCKS server using Dante 179 | prefixWith "[DANTE]" echo "Generating configuration file" 180 | # replace ${SOCKS_METHOD} in dante configuration with the authentication mode 181 | sed -i "s/\${SOCKS_METHOD}/$([[ "${authentication}" == true ]] && echo username || echo none)/" /etc/danted.conf 182 | ) 183 | 184 | prefixWith "[DANTE]" echo "Starting Danted server" 185 | prefixWith "[DANTE]" danted 186 | 187 | } 2>&1 1>&3 3>&- | { while read -r line; do echo "$(date '+%Y-%m-%d %H:%M:%S') ERR $line"; done } } 3>&1 1>&2 | { while read -r line; do echo "$(date '+%Y-%m-%d %H:%M:%S') OUT $line"; done } 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Dockerized Windscribe SOCKS5 Server 3 |

4 | 5 |

6 | 7 | repository license 8 | 9 | 10 | release version 11 | 12 | 13 | Docker pulls 14 | 15 |

16 | 17 |

18 | Containerizes a SOCKS5 proxy server with traffic tunneled through Windscribe's VPN service 19 |

20 | 21 | ## Table of Contents 22 | - [Motivations](#motivations) 23 | - [Pro Et Contra](#pro-et-contra) 24 | - [Advantages](#advantages) 25 | - [Limitations](#limitations) 26 | - [Deployment](#deployment) 27 | - [Image Source](#image-source) 28 | - [Deploying Container](#deploying-container) 29 | - [Docker Compose](#docker-compose) 30 | - [Docker CLI](#docker-cli) 31 | - [Configuration](#configuration) 32 | - [Disclaimer](#disclaimer) 33 | 34 | ## Motivations 35 | [Windscribe](https://windscribe.com/) is a yet another VPN service, offering varying subscriptions plans (free, pro, "build a plan", etc). Typically, [Windscribe software](https://windscribe.com/download) must be installed on host devices to tunnel traffic through their VPN servers. However, there are [other protocols](https://windscribe.com/features/config-generators) (e.g. OpenVPN, IKEv2, SOCKS5, etc) supported for tunneling *without* their proprietary software. Unfortunately, these protocols are only available to users on their "Pro" subscription plan (i.e. excluding free and "Build A Plan" subscription plans). 36 | 37 | I had submitted a feature request for SOCKS5 support for the "Build A Plan" option from their support, but have received a generic response indicating there was no particular interest in adding such support for non-"Pro" subscription plans. Ergo, Windscribe software must be installed on a host device to tunnel traffic, presenting two corollaries: 38 | - a host device must be eligible for installing and running Windscribe VPN software 39 | - _all_ system traffic will be tunneled through Windscribe servers 40 | 41 | This project was created to address a fringe use-case and circumvent the aforementioned corollaries by containerizing Windscribe software within [Docker](https://www.docker.com/), enabling tunneling through as a SOCKS5 proxy server. 42 | 43 | 44 | ## Pro Et Contra 45 | ### Advantages 46 | There are a few useful advantages of using this containerized application: 47 | - Paid subscriptions are not required to use the SOCKS5 protocol to tunnel traffic through Windscribe. 48 | - A host device does not need to install Windscribe system software and can still tunnel traffic through their VPN servers. 49 | - Networking tools (e.g. [Proxifier](https://www.proxifier.com/)) can enable fine-grained control by handling per-process traffic tunneling, rather than system wide traffic tunneling. 50 | 51 | ### Limitations 52 | However, there limitations to this project's usefulness relating significantly to security: 53 | - Traffic to the SOCKS5 server is _not_ encrypted and may be interceptable by a third party; however, traffic forwarded to Windscribe is encrypted. 54 | - Without authentication, the SOCKS5 server should _only_ be used in a tightly controlled network. Exposing the SOCKS5 server publicly allows any actor to tunnel traffic that is linked back to the specified Windscribe account. As of version `0.3.0`, proxy server authentication can be configured through environment variables. 55 | - [Windscribe-CLI](https://windscribe.com/guides/linux) requires `iptables` support, requiring the `NET_ADMIN` cap permission to execute inside of a Docker container. As a consequence, a compromised container may be able to leverage all the capabilities of `CAP_NET_ADMIN`, as defined in the [Linux manuals](http://man7.org/linux/man-pages/man7/capabilities.7.html). While it is unlikely the software involved would be compromised, there is a non-zero possibility that a compromised container may be able to manipulate the host's iptables for malicious purposes. 56 | 57 | 58 | ## Deployment 59 | This project must be built using a container image building tool and run using container runtime (e.g. Docker, Podman, etc). [Docker](https://www.docker.com/) instructions are included in the following sections. 60 | 61 | ### Image Source 62 | Pre-built images can be pulled from any of the following registries: 63 | - [Docker Hub](https://hub.docker.com/r/concisions/windscribe-socks-server): `concisions/windscribe-socks-server:latest` 64 | - [GitHub Packages](https://github.com/concision/docker-windscribe-socks-server/packages): `docker.pkg.github.com/concision/docker-windscribe-socks-server/windscribe-socks-server:latest` 65 | > Note: The only prebuilt images architectures available are `linux/amd64` and `linux/arm/v7`. At the time of writing this documentation, Windscribe distributions are not available for other architectures. 66 | 67 | Alternatively, the project can be built from the repository's sources by cloning the repository and running a container image build tool. 68 | ```bash 69 | # clone the repository 70 | git clone https://github.com/concision/docker-windscribe-socks-server.git 71 | # change current working directory 72 | cd docker-windscribe-socks-server 73 | # build Docker image 74 | docker build -t concisions/windscribe-socks-server:latest . 75 | ``` 76 | > Note: Ensure the current working directory is inside of the cloned Git repository prior to executing the command (e.g. `cd docker-windscribe-socks-server`). 77 | 78 | ### Deploying Container 79 | #### Docker Compose 80 | To deploy with [Docker Compose](https://docs.docker.com/compose/), use the commented configuration file available in this repository [here](https://github.com/concision/docker-windscribe-socks-server/blob/master/docker-compose.yml). Environment variables may be sourced with an `.env` file or explicitly defined in the configuration file. 81 | 82 | The container can be deployed with the following command: 83 | ```bash 84 | docker-compose up 85 | ``` 86 | 87 | > Note: An `.env` file containing all environment variable configuration can be passed as a [Docker secret](https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose) file using the key `windscribe_server`. Uncomment the relevant section in the [`docker-compose.yml`](https://github.com/concision/docker-windscribe-socks-server/blob/master/docker-compose.yml) file template. Configured Docker secrets take precedence over environment variables. 88 | 89 | #### Docker CLI 90 | To deploy with [Docker](https://www.docker.com/), use the example run script available in this repository [here](https://github.com/concision/docker-windscribe-socks-server/blob/master/deploy-container.sh). It can be configured in the script itself or use an `.env` file. 91 | 92 | The container can be deployed with the following command: 93 | ```bash 94 | ./deploy-container.sh 95 | ``` 96 | > Note: If specifying multiple SOCKS5 users, specify the relevant environment variables in an `.env` file or add `--env SOCKS_USERNAME_xyz` and `--env SOCKS_PASSWORD_xyz` flag (where "xyz" is a wildcard) to the script. 97 | 98 | ### Configuration 99 | There are several variables that can be configured for this image: 100 | - **Windscribe**: 101 | - `WINDSCRIBE_DNS` (optional): Whitespace delimited list of DNS servers to use (default: `1.1.1.1`). Setting a DNS server with Docker flags is not sufficient enough, as it utilizes an embedded local DNS server. Windscribe tunnels all DNS requests to prevent DNS leakage. 102 | - `WINDSCRIBE_USERNAME`: Windscribe account username. 103 | - `WINDSCRIBE_PASSWORD`: Windscribe account password. 104 | - `WINDSCRIBE_LOCATION` (optional): A preferred Windscribe location to automatically connect to. 105 | - **SOCKS5 Server**: 106 | > Note: By default, there is no authentication enabled. Setting any of the environment variables `SOCKS_USERNAME` or `SOCKS_USERNAME_xyz` automatically enables authentication. Without authentication, the SOCKS5 server should _only_ be used in a tightly controlled network. 107 | - `SOCKS_USERNAME` (optional): Enables SOCKS5 authentication and creates a new user. Must be alphanumeric (with `_`s). 108 | - `SOCKS_PASSWORD` (optional): Enables SOCKS5 authentication and sets the password for the associated `$SOCKS_USERNAME` user. 109 | Additional users can be defined by namespacing (e.g. suffixing "_1") additional environment variables under pairs of `SOCKS_USERNAME` and `SOCKS_PASSWORD`: 110 | - `SOCKS_USERNAME_xyz` (optional): Enables SOCKS5 authentication and creates a new user. Must be alphanumeric (with `_`s). 111 | - `SOCKS_PASSWORD_xyz` (optional): Enables SOCKS5 authentication and sets the password for the associated `SOCKS_USERNAME_xyz` user. 112 | 113 | ## Disclaimer 114 | This project is a prototype that has been hacked together and has its own set of issues and drawbacks compared to running Windscribe system software. Your mileage may vary. If you are experiencing an issue you believe is not intended, a GitHub issue can be filed [here](https://github.com/concision/docker-windscribe-socks-server/issues/new); however, not all issues may be solvable due to the hacky and unpredictable nature of this project and its software dependencies. 115 | --------------------------------------------------------------------------------