Skip to content

Commit

Permalink
Put shadowsocks behind nginx
Browse files Browse the repository at this point in the history
  • Loading branch information
ed-asriyan committed May 19, 2024
1 parent 03d4793 commit 752f7cd
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 10 deletions.
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,38 @@ This is deployment for my personal server with [outline](http://getoutline.org)/
## Shadowsocks clients that work with this setup
https://getoutline.org/get-started/#step-3

# Architecture diagram
# Architecture
![digram](./diagram.svg)

**Frontman** is linux host seith static server serving via HTTPS
* Static html pages with installation instructions
* Dynamic ShadowSocks configuration ([SIP008](https://shadowsocks.org/doc/sip008.html))

**Proxy 1..N** are linux hosts with installed
* [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server): shadowsocks implementation made by https://jigsaw.google.com that supports multiple access keys
There are two types of hosts: **frontman** and **proxy**. Frontman should be deployed as a single instance (sharding
under single DNS record or IP address is allowed). Proxies could be deployed as many instances as needed, each instance
should have dedicated IP address and DNS record (if exists). All hosts should be Debian hosts with public IPs.

## Frontman
It is a single linux host serving static content over HTTPS:
* Static html pages with installation instructions. User opens a received link to get an instruction with personal
ShadowSocks configuration
* Personal dynamic ShadowSocks configuration ([SIP008](https://shadowsocks.org/doc/sip008.html)) for each client
* [certbot](https://certbot.eff.org) for automatic and free renewal of HTTPS certificates

Playbook: [frontman.yml](./frontman.yml)

## Proxy
As many proxy hosts as needed could be deployed but each one should have its own IP address and/or DNS record.
Proxy(ies) is/are linux host(s) with installed
* [nginx](https://nginx.org) that proxies traffic:
* if connection is recognized as TLS, request is handled as HTTPS connection
* otherwise; connection is proxied to Shadosocks
* [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server): Shadowsocks implementation made by
https://jigsaw.google.com that supports multiple access keys
* [prometheus](https://prometheus.io): monitoring to detect traffic abuse
* [node-exporter](https://github.com/prometheus/node_exporter): Prometheus exporter for hardware and OS metrics

Playbook: [proxies.yml](./proxies.yml)

# Setup
This part requires [Ansible](https://www.ansible.com) knowledge.
# Development
This part requires [Ansible](https://www.ansible.com) knowledge. The deployment is tested on and implemented for Debian
only.

## At the very beginning
1. Initialize pre-commit hook to prevent secrets from being leaked:
Expand Down
1 change: 1 addition & 0 deletions proxies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
no_log: true
roles:
- shadowsocks
- shadowsocks-gateway
- prometheus
38 changes: 38 additions & 0 deletions roles/nginx/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# http://nginx.org/en/linux_packages.html#Debian

- name: Check if nginx is installed
shell: nginx -v
register: nginx_installed
ignore_errors: yes

- name: Install the prerequisites
with_items:
- curl
- gnupg2
- ca-certificates
- lsb-release
- debian-archive-keyring
package:
name: "{{ item }}"
state: present
when: nginx_installed.failed

- name: Import an official nginx signing key so apt could verify the packages authenticity
command: bash -c "curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg > /dev/null"
when: nginx_installed.failed

- name: Set up the apt repository for stable nginx packages
command: bash -c "echo 'deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/debian `lsb_release -cs` nginx' | tee /etc/apt/sources.list.d/nginx.list"
when: nginx_installed.failed

- name: Install nginx
package:
name: nginx
state: present
when: nginx_installed.failed

- name: Stop nginx
systemd:
name: nginx.service
state: stopped
enabled: false
3 changes: 3 additions & 0 deletions roles/shadowsocks-gateway/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
- role: nginx
- role: shadowsocks
41 changes: 41 additions & 0 deletions roles/shadowsocks-gateway/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
- name: Install libnginx-mod-stream
package:
name: libnginx-mod-stream
state: present

- name: Create user
user: name={{ shadowsocks_gateway_user }}

- name: Render nginx config
template:
src: nginx.conf.j2
dest: "/home/{{ shadowsocks_gateway_user }}/nginx.conf"
group: "{{ shadowsocks_gateway_user }}"
owner: "{{ shadowsocks_gateway_user }}"
mode: "600"
register: config

- name: Remove unexpected files in home
include_tasks: tasks/remove-unexpected-files.yml
vars:
directory: "/home/{{ shadowsocks_gateway_user }}"
files:
- nginx.conf

- name: Render systemd service config
template:
src: shadowsocks-gateway.service.j2
dest: /etc/systemd/system/shadowsocks-gateway.service
register: systemd

- name: Reload daemon
systemd:
daemon_reload: yes
when: systemd.changed

- name: Restart systemd app service
systemd:
name: shadowsocks-gateway.service
state: restarted
enabled: yes
when: systemd.changed or config.changed
40 changes: 40 additions & 0 deletions roles/shadowsocks-gateway/templates/nginx.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
daemon off;
worker_processes auto;

events {
worker_connections 1024;
}

http {
server {
listen 80 default_server;
server_name _;
return 301 https://{{ shadowsocks_gateway_fallback_proxy_target }};
}
}

stream {
map $ssl_preread_protocol $backend {
default shadowsocks_backend;
"TLSv1.0" https_backend;
"TLSv1.2" https_backend;
"TLSv1.3" https_backend;
}

upstream https_backend {
server {{ shadowsocks_gateway_fallback_proxy_target }};
}

upstream shadowsocks_backend {
server localhost:{{ shadowsocks_port }};
}

server {
listen {{ server.port }};
proxy_timeout 5s;
ssl_preread on;

proxy_pass $backend;
}
}
11 changes: 11 additions & 0 deletions roles/shadowsocks-gateway/templates/shadowsocks-gateway.service.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[Unit]
Description=shadowsocks-gateway
After=shadowsocks.service

[Service]
User=root
ExecStart=nginx -c /home/{{ shadowsocks_gateway_user }}/nginx.conf
Restart=always

[Install]
WantedBy=multi-user.target
10 changes: 10 additions & 0 deletions roles/shadowsocks-gateway/vars/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# linux user to run shadowsocks-gateway as
shadowsocks_gateway_user: shadowsocks-gateway
# where regular https requests (non-shadowsocks requests) proxy to
shadowsocks_gateway_fallback_proxy_target: !vault |
$ANSIBLE_VAULT;1.1;AES256
37333637313939636530653837626436396562306537353834326662383835353162393962336439
6430373734363064656263373964363839643862326433660a383630663561313838666335343462
65376263643234313439393933393262393932323664653030616432303439363164666539636362
6139373831393530360a663130336433373266333635303637393735313932336131326162373734
3066
2 changes: 1 addition & 1 deletion roles/shadowsocks/vars/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ shadowsocks_replay_history: 10000
# port of metrics for prometheus to be exposed
shadowsocks_metrics_port: 9091
# port of shadowsocks behind gateway
shadowsocks_port: 443
shadowsocks_port: 46365

0 comments on commit 752f7cd

Please sign in to comment.