diff --git a/README.md b/README.md index cee6572..1b35735 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Proxy [](https://github.com/ed-asriyan/proxy-server/actions/workflows/CI-pre-commit.yml) [](https://github.com/ed-asriyan/proxy-server/actions/workflows/CD-production.yml) -This is deployment for my personal server with [outline](http://getoutline.org)/[shadowsocks](http://shadowsocks.org) on board for me and my friends to bypass internet censorship. +# Proxy [](https://github.com/ed-asriyan/xray-server/actions/workflows/CI-pre-commit.yml) [](https://github.com/ed-asriyan/xray-server/actions/workflows/CD-production.yml) +This is deployment for my personal server with [xray](https://xtls.github.io/en/) on board for me and my friends to bypass internet censorship. -## Shadowsocks clients that work with this setup -https://getoutline.org/get-started/#step-3 +## Vless clients that work with this setup +https://hiddify.com#app # Architecture  @@ -13,8 +13,13 @@ each instance should have dedicated IP address and DNS record (if exists). All h ## GH Pages Serves static content: -* static html pages with installation instructions. The user is provided with a private instruction link with a personal ShadowSocks configuration, which the user uses once to install the ShadowSocks configuration -* personal dynamic ShadowSocks configuration json files ([SIP008](https://shadowsocks.org/doc/sip008.html)) for each client, which is used by ShadowSocks client each time before connecting to a ShadowSocks server +* static html pages with installation instructions. The user is provided with a private instruction link with a personal vless +[subscription URL](https://hiddify.com/app/URL-Scheme), which the user uses once to install the vless configuration +* personal vless [subscription files](https://hiddify.com/app/URL-Scheme) for each client, which is used by Hiddify to refresh +list of available servers + +Playbook: [users-configs.yml](./users-configs.yml). It just renders files locally, the should be uploaded got GitHub Pages using +Actions. ## Metrics It is a single linux host with the prometheus installed. Users do not access this host. Host may have no domain name. @@ -25,14 +30,15 @@ Playbook: [metrics.yml](./metrics.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 -* [outline-ss-server](https://github.com/Jigsaw-Code/outline-ss-server) (role: `shadowsocks`): Shadowsocks implementation made by -https://jigsaw.google.com that supports multiple access keys -* [node-exporter](https://github.com/prometheus/node_exporter) (role: `node-exporter`): Prometheus exporter for hardware and OS metrics -* [nginx](https://nginx.org) (role: `shadowsocks-gateway`) that proxies traffic: - * port `config_servers[uuid].port`: - * if connection is recognized as TLS, request is handled as HTTPS connection - * otherwise; connection is proxied to ShadoSocks server - * port `config_servers[uuid].prometheus_metrics.port`: to outline-ss-server and node-exporter metrics endpoints +* [xray-core](https://github.com/xtls/xray-core) (role: `xray`) that proxies traffic: + * if it's valid vless connection, to the destination + * otherwise, to `server.fallback_proxy_target` +* [node-exporter](https://github.com/prometheus/node_exporter) (role: `node-exporter`): Prometheus exporter for hardware and OS +metrics. Exports metrics to TCP port available from localhost only +* xray-exporter (role: `node-exporter`): custom script that exports xray metrics. Exports metrics to TCP port available from +localhost only +* [nginx](https://nginx.org) (role: `metrics-exporter`) that proxies https requests on +`config_servers[uuid].prometheus_metrics.port` TCP port to *node-exporter* and *xray-exporter*: Playbook: [proxies.yml](./proxies.yml) @@ -69,7 +75,7 @@ Read code and find out ## How to apply changes to production * If you changed deploy code: just push to master branch. GitHub Actions will automatically apply updates to the servers. -* If you changed list of users: manually trigger [CD | Production](https://github.com/ed-asriyan/proxy-server/actions/workflows/CD-production.yml) +* If you changed list of users: manually trigger [CD | Production](https://github.com/ed-asriyan/xray-server/actions/workflows/CD-production.yml) # Development ## CD diff --git a/config/hosts.example.yml b/config/hosts.example.yml index 893ed46..585de12 100644 --- a/config/hosts.example.yml +++ b/config/hosts.example.yml @@ -6,9 +6,9 @@ all: # linux user to connect to ssh to the server as ansible_user: proxy1: - # uuid of the shadowsocks server that should be deployed (from config/servers.yml) + # uuid of the xray server that should be deployed (from config/servers.yml) hosts_server_uuid: - # public domain or IP where shadowsocks should be deployed (if ssh and shadowsocks are on the same IP, it should be equal to config_servers[hosts_server_uuid].host value) + # public domain or IP where xray should be deployed (if ssh and xray are on the same IP, it should be equal to config_servers[hosts_server_uuid].host value) ansible_host: # linux user to connect to ssh to the server as ansible_user: diff --git a/config/servers.example.yml b/config/servers.example.yml index ef19d42..35bb1e4 100644 --- a/config/servers.example.yml +++ b/config/servers.example.yml @@ -1,55 +1,50 @@ config_servers: server1-uuid: - # domain of server that will be put into config + # domain of server that will be put into config (IP or domain users will connect to) host: - # description or name of server - remarks: - # port of shadowsocks to be exposed and used publicly + # name of server (must be URL-encode safe) + name: + # port of xray to be exposed and used publicly port: - # cipher to use in shadowsocks - method: - # secret (any random string) to be used for generating client passwords - secret: - # prefix to use. read more: https://www.reddit.com/r/outlinevpn/wiki/index/prefixing - prefix: - # where regular https requests (non-shadowsocks requests) proxy to - fallback_proxy_target: - # if you don't want to install prometheus on the server, replace yaml object to boolean false: - # prometheus_metrics: false - prometheus_metrics: - # port where prometheus metrics endpoints should be exposed - port: - # content of self-signed cert and keys (SSL pem format) - tls: - certificate: | - -----BEGIN CERTIFICATE----- - .... - -----END CERTIFICATE----- - key: | - -----BEGIN PRIVATE KEY----- - .... - -----END PRIVATE KEY----- - shadowsocks: - # any random URL-valid string starting with "/" - url_path: - node_exporter: - # any random URL-valid string starting with "/" - url_path: + # flow parameter in https://xtls.github.io/en/config/outbounds/vless.html#serverobject (ideally xtls-rprx-vision) + flow: + # fingerprint parameters as `fingerprint` in https://xtls.github.io/ru/config/transport.html#tlsobject + fingerprints: + - chrome + - safari + - "..." + # private key. generate with ./xray x25519 + private_key: + # public key. generate with ./xray x25519 + public_key: + # where regular https requests proxy to and what website xray should pretent as + fallback_proxy_target: example.com + # list of sni should be accepter by xray. see serverNames in https://xtls.github.io/ru/config/transport.html#realityobject + supported_snis: + - example.com + - www.example.com + # second server (for instance) server2-uuid: + # domain of server that will be put into config (IP or domain users will connect to) host: - remarks: + # name of server (must be URL-encode safe) + name: + # port of xray to be exposed and used publicly port: - method: - secret: - prefix: + # flow parameter in https://xtls.github.io/en/config/outbounds/vless.html#serverobject (ideally xtls-rprx-vision) + flow: + # fingerprint parameter in https://xtls.github.io/ru/config/transport.html#tlsobject + fingerprints: + - + - + # private key. generate with ./xray x25519 + private_key: + # public key. generate with ./xray x25519 + public_key: + # where regular https requests proxy to and what website xray should pretent as fallback_proxy_target: - prometheus_metrics: - port: - tls: - certificate_path: - key_path: - shadowsocks: - url_path: - node_exporter: - url_path: + # list of sni should be accepter by xray. see serverNames in https://xtls.github.io/ru/config/transport.html#realityobject + supported_snis: + - + - diff --git a/config/users-configs.example.yml b/config/users-configs.example.yml index 62993e1..65cc51f 100644 --- a/config/users-configs.example.yml +++ b/config/users-configs.example.yml @@ -1,2 +1,8 @@ # where redirect to if user opened index page without valid parameters config_users_configs_default_redirect: + +# title users should we when open hiddify +config_users_title: + +# support url. see https://hiddify.com/app/URL-Scheme +config_users_support_url: diff --git a/diagram.svg b/diagram.svg index bfd293e..8109075 100644 --- a/diagram.svg +++ b/diagram.svg @@ -1,4 +1,4 @@ - + diff --git a/proxies.yml b/proxies.yml index 27aa08a..5aee14c 100644 --- a/proxies.yml +++ b/proxies.yml @@ -4,9 +4,27 @@ vars_files: - ./config/users.yml - ./config/servers.yml + vars: + # do not worry. these ports are local + config_local_xray_metrics_port: 8081 + config_local_node_exporter_metrics_port: 8082 + config_local_xray_exporter_metrics_port: 8080 roles: - - role: shadowsocks-gateway + - role: xray vars: - shadowsocks_gateway_server_uuid: "{{ hosts_server_uuid }}" - shadowsocks_gateway_server: "{{ config_servers[shadowsocks_gateway_server_uuid] }}" - shadowsocks_gateway_users: "{{ config_users }}" + xray_server_uuid: "{{ hosts_server_uuid }}" + xray_server: "{{ config_servers[xray_server_uuid] }}" + xray_users: "{{ config_users }}" + xray_metrics_port: "{{ config_local_xray_metrics_port }}" + - role: node-exporter + vars: + node_exporter_port: "{{ config_local_node_exporter_metrics_port }}" + - role: xray-exporter + vars: + xray_exporter_port: "{{ config_local_xray_exporter_metrics_port }}" + xray_exporter_fetch_port: "{{ config_local_xray_metrics_port }}" + - role: metrics-exporter + vars: + metrics_exporter_server: "{{ config_servers[hosts_server_uuid] }}" + metrics_exporter_node_exporter_metrics_port: "{{ config_local_node_exporter_metrics_port }}" + metrics_exporter_xray_metrics_port: "{{ config_local_xray_exporter_metrics_port }}" diff --git a/roles/shadowsocks-gateway/README.md b/roles/metrics-exporter/README.md similarity index 88% rename from roles/shadowsocks-gateway/README.md rename to roles/metrics-exporter/README.md index 2971195..a164e9a 100644 --- a/roles/shadowsocks-gateway/README.md +++ b/roles/metrics-exporter/README.md @@ -1,3 +1,3 @@ -# shadowsocks-gateway +# metrics exporter ## Mandatory and optional variables Find them in [./defaults/main.yml](./defaults/main.yml). Empty variables in the file are mandatory, pre-filled variables are optional. diff --git a/roles/metrics-exporter/defaults/main.yml b/roles/metrics-exporter/defaults/main.yml new file mode 100644 index 0000000..cad751a --- /dev/null +++ b/roles/metrics-exporter/defaults/main.yml @@ -0,0 +1,8 @@ +# one(!) server item as secribed in /servers-example.yml +metrics_exporter_server: + +# localhost port xray metrics are available on +metrics_exporter_xray_metrics_port: + +# localhost port node_exporter metrics are available on +metrics_exporter_node_exporter_metrics_port: diff --git a/roles/metrics-exporter/meta/main.yml b/roles/metrics-exporter/meta/main.yml new file mode 100644 index 0000000..69891c7 --- /dev/null +++ b/roles/metrics-exporter/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - role: nginx diff --git a/roles/metrics-exporter/tasks/main.yml b/roles/metrics-exporter/tasks/main.yml new file mode 100644 index 0000000..73450b6 --- /dev/null +++ b/roles/metrics-exporter/tasks/main.yml @@ -0,0 +1,74 @@ + +- name: Ensure required variables are defined + assert: + that: + - metrics_exporter_xray_metrics_port is number + - metrics_exporter_node_exporter_metrics_port is number + +- name: Ensure metrics_exporter_server is defined correctly + include_tasks: tasks/assert-server.yml + vars: + server: "{{ metrics_exporter_server }}" + +- name: Create user + user: name={{ metrics_exporter_user }} + +- name: Render nginx config + template: + src: nginx.conf.j2 + dest: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_nginx_conf_filename }}" + group: "{{ metrics_exporter_user }}" + owner: "{{ metrics_exporter_user }}" + mode: "600" + register: config + +- name: Copy SSL key + copy: + content: "{{ metrics_exporter_server.prometheus_metrics.tls.key }}" + dest: "{{ metrics_exporter_key_path }}" + group: "{{ metrics_exporter_user }}" + owner: "{{ metrics_exporter_user }}" + mode: "600" + when: metrics_exporter_server.prometheus_metrics is mapping + register: ssl_key + +- name: Copy SSL certificate + copy: + content: "{{ metrics_exporter_server.prometheus_metrics.tls.certificate }}" + dest: "{{ metrics_exporter_certificate_path }}" + group: "{{ metrics_exporter_user }}" + owner: "{{ metrics_exporter_user }}" + mode: "600" + when: metrics_exporter_server.prometheus_metrics is mapping + register: ssl_cert + +- name: Remove unexpected files in home + include_tasks: tasks/remove-unexpected-files.yml + vars: + directory: "/home/{{ metrics_exporter_user }}" + files: + - nginx.conf + - "{{ metrics_exporter_certificate_filename }}" + - "{{ metrics_exporter_key_filename }}" + - "{{ metrics_exporter_pid_filename }}" + - "{{ metrics_exporter_nginx_conf_filename }}" + - "{{ metrics_exporter_access_log_filename }}" + - "{{ metrics_exporter_error_log_filename }}" + +- name: Render systemd service config + template: + src: metrics-exporter.service.j2 + dest: /etc/systemd/system/metrics-exporter.service + register: systemd + +- name: Reload daemon + systemd: + daemon_reload: yes + when: systemd.changed + +- name: Restart systemd app service + systemd: + name: metrics-exporter.service + state: restarted + enabled: yes + when: systemd.changed or config.changed or ssl_key.changed or ssl_cert.changed diff --git a/roles/metrics-exporter/templates/metrics-exporter.service.j2 b/roles/metrics-exporter/templates/metrics-exporter.service.j2 new file mode 100644 index 0000000..5e65b32 --- /dev/null +++ b/roles/metrics-exporter/templates/metrics-exporter.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=metrics-exporter + +[Service] +User={{ metrics_exporter_user }} +ExecStart=/usr/sbin/nginx -c {{ metrics_exporter_nginx_conf_path }} +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/roles/metrics-exporter/templates/nginx.conf.j2 b/roles/metrics-exporter/templates/nginx.conf.j2 new file mode 100644 index 0000000..cfa6bbf --- /dev/null +++ b/roles/metrics-exporter/templates/nginx.conf.j2 @@ -0,0 +1,49 @@ +daemon off; +worker_processes auto; + +pid {{ metrics_exporter_pid_path }}; + +events { + worker_connections 4096; +} + +http { + access_log {{ metrics_exporter_access_log_path }}; + error_log {{ metrics_exporter_error_log_path }}; + + server { + listen *:80; + listen [::]:80; + server_name _; + return 301 https://{{ metrics_exporter_server.fallback_proxy_target }}; + } + +{% if metrics_exporter_server.prometheus_metrics is mapping %} + server { + listen *:{{ metrics_exporter_server.prometheus_metrics.port }} ssl http2; + listen [::]:{{ metrics_exporter_server.prometheus_metrics.port }} ssl http2; + + ssl_certificate {{ metrics_exporter_certificate_path }}; + ssl_certificate_key {{ metrics_exporter_key_path }}; + ssl_protocols TLSv1.2 TLSv1.3; + + location {{ metrics_exporter_server.prometheus_metrics.xray.url_path }} { + proxy_pass http://127.0.0.1:{{ metrics_exporter_xray_metrics_port }}/metrics; + } + + location {{ metrics_exporter_server.prometheus_metrics.node_exporter.url_path }} { + proxy_pass http://127.0.0.1:{{ metrics_exporter_node_exporter_metrics_port }}/metrics; + } + + location / { + return 444; + } + + error_page 404 = @redirect; + + location @redirect { + return 444; + } + } +{% endif %} +} \ No newline at end of file diff --git a/roles/metrics-exporter/vars/main.yml b/roles/metrics-exporter/vars/main.yml new file mode 100644 index 0000000..f5846b4 --- /dev/null +++ b/roles/metrics-exporter/vars/main.yml @@ -0,0 +1,16 @@ +# linux user to run metrics_exporter as +metrics_exporter_user: metrics-exporter + +metrics_exporter_nginx_conf_filename: nginx.conf +metrics_exporter_certificate_filename: cert.pem +metrics_exporter_key_filename: key.pem +metrics_exporter_access_log_filename: access.log +metrics_exporter_error_log_filename: error.log +metrics_exporter_pid_filename: nginx.pid + +metrics_exporter_nginx_conf_path: /home/{{ metrics_exporter_user }}/{{metrics_exporter_nginx_conf_filename }} +metrics_exporter_certificate_path: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_certificate_filename }}" +metrics_exporter_key_path: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_key_filename }}" +metrics_exporter_access_log_path: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_access_log_filename }}" +metrics_exporter_error_log_path: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_error_log_filename }}" +metrics_exporter_pid_path: "/home/{{ metrics_exporter_user }}/{{ metrics_exporter_pid_filename }}" diff --git a/roles/node-exporter/defaults/main.yml b/roles/node-exporter/defaults/main.yml index 4761ae6..b279b44 100644 --- a/roles/node-exporter/defaults/main.yml +++ b/roles/node-exporter/defaults/main.yml @@ -1,2 +1,2 @@ # the port node_exporter should locally (localhost) export http metrics on -node_exporter_port: 9092 +node_exporter_port: diff --git a/roles/node-exporter/vars/main.yml b/roles/node-exporter/vars/main.yml index 8c11e4c..410b1be 100644 --- a/roles/node-exporter/vars/main.yml +++ b/roles/node-exporter/vars/main.yml @@ -1,7 +1,7 @@ # linus user to run node-exporter as node_exporter_user: node_exporter -# URL to download outline from +# URL to download node-exporter from node_exporter_downloads: x86_64: url: https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz diff --git a/roles/prometheus/templates/config.yml.j2 b/roles/prometheus/templates/config.yml.j2 index 4f53f1e..4290f69 100644 --- a/roles/prometheus/templates/config.yml.j2 +++ b/roles/prometheus/templates/config.yml.j2 @@ -5,11 +5,11 @@ scrape_configs: {% set server = server_item.value %} {% set server_uuid = server_item.key %} {% if server.prometheus_metrics is mapping %} - - job_name: 'shadowsocks-{{ server.host }}' + - job_name: 'xray-{{ server.host }}' scheme: https tls_config: ca_file: '/home/{{ prometheus_user }}/{{ prometheus_scrape_ssl_certs_dir_name }}/{{ server_uuid }}-{{ prometheus_scrape_ssl_cert_filename_suffix }}' - metrics_path: '{{ server.prometheus_metrics.shadowsocks.url_path }}' + metrics_path: '{{ server.prometheus_metrics.xray.url_path }}' static_configs: - targets: - '{{ server.host }}:{{ server.prometheus_metrics.port }}' diff --git a/roles/prometheus/templates/prometheus.service.j2 b/roles/prometheus/templates/prometheus.service.j2 index b32176e..778efcd 100644 --- a/roles/prometheus/templates/prometheus.service.j2 +++ b/roles/prometheus/templates/prometheus.service.j2 @@ -1,6 +1,5 @@ [Unit] Description=prometheus -After=outline.service [Service] User={{ prometheus_user }} diff --git a/roles/shadowsocks-gateway/defaults/main.yml b/roles/shadowsocks-gateway/defaults/main.yml deleted file mode 100644 index a38e38c..0000000 --- a/roles/shadowsocks-gateway/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# server uuid as in /servers-example.yml -shadowsocks_server_uuid: -# one(!) server item as secribed in /servers-example.yml -shadowsocks_server: -# users as described in /users-example.yml -shadowsocks_users: diff --git a/roles/shadowsocks-gateway/meta/main.yml b/roles/shadowsocks-gateway/meta/main.yml deleted file mode 100644 index ce34493..0000000 --- a/roles/shadowsocks-gateway/meta/main.yml +++ /dev/null @@ -1,14 +0,0 @@ -dependencies: - - role: nginx - - role: shadowsocks - vars: - shadowsocks_listen_address: 127.0.0.1 - shadowsocks_port: "{{ shadowsocks_gateway_shadowsocks_port }}" - shadowsocks_metrics_port: "{{ shadowsocks_gateway_shadowsocks_metrics_port }}" - shadowsocks_server_uuid: "{{ shadowsocks_gateway_server_uuid }}" - shadowsocks_server: "{{ shadowsocks_gateway_server }}" - shadowsocks_users: "{{ shadowsocks_gateway_users }}" - - role: node-exporter - when: shadowsocks_gateway_server.prometheus_metrics is mapping - vars: - node_exporter_port: "{{ shadowsocks_gateway_node_exporter_port }}" diff --git a/roles/shadowsocks-gateway/tasks/main.yml b/roles/shadowsocks-gateway/tasks/main.yml deleted file mode 100644 index 4516dc9..0000000 --- a/roles/shadowsocks-gateway/tasks/main.yml +++ /dev/null @@ -1,87 +0,0 @@ -- name: Ensure required variables are defined - assert: - that: - - shadowsocks_gateway_server_uuid is string - -- name: Ensure shadowsocks_gateway_users is defined correctly - include_tasks: tasks/assert-users.yml - vars: - users: "{{ shadowsocks_gateway_users }}" - -- name: Ensure shadowsocks_gateway_server is defined correctly - include_tasks: tasks/assert-server.yml - vars: - server: "{{ shadowsocks_gateway_server }}" - -- 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: Copy SSL key - copy: - content: "{{ shadowsocks_gateway_server.prometheus_metrics.tls.key }}" - dest: "{{ shadowsocks_gateway_key_path }}" - group: "{{ shadowsocks_gateway_user }}" - owner: "{{ shadowsocks_gateway_user }}" - mode: "600" - when: shadowsocks_gateway_server.prometheus_metrics is mapping - register: ssl_key - -- name: Copy SSL certificate - copy: - content: "{{ shadowsocks_gateway_server.prometheus_metrics.tls.certificate }}" - dest: "{{ shadowsocks_gateway_certificate_path }}" - group: "{{ shadowsocks_gateway_user }}" - owner: "{{ shadowsocks_gateway_user }}" - mode: "600" - when: shadowsocks_gateway_server.prometheus_metrics is mapping - register: ssl_cert - -- name: Remove unexpected files in home - include_tasks: tasks/remove-unexpected-files.yml - vars: - directory: "/home/{{ shadowsocks_gateway_user }}" - files: - - nginx.conf - - "{{ shadowsocks_gateway_certificate_filename }}" - - "{{ shadowsocks_gateway_key_filename }}" - - "{{ shadowsocks_gateway_pid_filename }}" - - "{{ shadowsocks_gateway_nginx_conf_filename }}" - - "{{ shadowsocks_gateway_http_access_log_filename }}" - - "{{ shadowsocks_gateway_http_error_log_filename }}" - - "{{ shadowsocks_gateway_tls_access_log_filename }}" - - "{{ shadowsocks_gateway_tcp_error_log_filename }}" - - "{{ shadowsocks_gateway_udp_error_log_filename }}" - - "{{ shadowsocks_gateway_prometheus_access_log_filename }}" - - "{{ shadowsocks_gateway_prometheus_error_log_filename }}" - -- 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 or ssl_key.changed or ssl_cert.changed diff --git a/roles/shadowsocks-gateway/templates/nginx.conf.j2 b/roles/shadowsocks-gateway/templates/nginx.conf.j2 deleted file mode 100644 index be05d23..0000000 --- a/roles/shadowsocks-gateway/templates/nginx.conf.j2 +++ /dev/null @@ -1,111 +0,0 @@ -load_module /usr/lib/nginx/modules/ngx_stream_module.so; -daemon off; -worker_processes auto; - -# https://www.cyberciti.biz/faq/linux-unix-nginx-too-many-open-files -worker_rlimit_nofile {{ shadowsocks_gateway_worker_rlimit_nofile }}; -pid {{ shadowsocks_gateway_pid_path }}; - -events { - worker_connections {{ shadowsocks_gateway_worker_connections }}; -} - -http { - server { - access_log {{ shadowsocks_gateway_http_access_log_path }}; - error_log {{ shadowsocks_gateway_http_error_log_path }}; - - listen *:80; - listen [::]:80; - server_name _; - return 301 https://{{ shadowsocks_gateway_server.fallback_proxy_target }}; - } - -{% if shadowsocks_gateway_server.prometheus_metrics is mapping %} - server { - access_log {{ shadowsocks_gateway_prometheus_access_log_path }}; - error_log {{ shadowsocks_gateway_prometheus_error_log_path }}; - - listen *:{{ shadowsocks_gateway_server.prometheus_metrics.port }} ssl http2; - listen [::]:{{ shadowsocks_gateway_server.prometheus_metrics.port }} ssl http2; - - ssl_certificate {{ shadowsocks_gateway_certificate_path }}; - ssl_certificate_key {{ shadowsocks_gateway_key_path }}; - ssl_protocols TLSv1.2 TLSv1.3; - - location {{ shadowsocks_gateway_server.prometheus_metrics.shadowsocks.url_path }} { - proxy_pass http://127.0.0.1:{{ shadowsocks_gateway_shadowsocks_metrics_port }}/metrics; - } - - location {{ shadowsocks_gateway_server.prometheus_metrics.node_exporter.url_path }} { - proxy_pass http://127.0.0.1:{{ shadowsocks_gateway_node_exporter_port }}/metrics; - } - - location / { - return 444; - } - - error_page 404 = @redirect; - - location @redirect { - return 444; - } - } -{% endif %} -} - -stream { - log_format basic '{' - '"time":"$time_iso8601"' - ',"remote_addr":"$remote_addr"' - ',"protocol":"$protocol"' - ',"status":"$status"' - ',"bytes_sent":"$bytes_sent"' - ',"bytes_received":"$bytes_received"' - ',"session_time":"$session_time"' - ',"upstream_addr":"$upstream_addr"' - ',"upstream_bytes_sent":"$upstream_bytes_sent"' - ',"upstream_bytes_received":"$upstream_bytes_received"' - ',"upstream_connect_time":"$upstream_connect_time"' - '}'; - - map $ssl_preread_protocol $backend { - default shadowsocks_backend; - "TLSv1.0" https_backend; - "TLSv1.2" https_backend; - "TLSv1.3" https_backend; - } - - map $ssl_preread_protocol $access_log_file { - default ../../../dev/null; - "TLSv1.0" ../../..{{ shadowsocks_gateway_tls_access_log_path }}; - "TLSv1.2" ../../..{{ shadowsocks_gateway_tls_access_log_path }}; - "TLSv1.3" ../../..{{ shadowsocks_gateway_tls_access_log_path }}; - } - - upstream https_backend { - server {{ shadowsocks_gateway_server.fallback_proxy_target }}; - } - - upstream shadowsocks_backend { - server 127.0.0.1:{{ shadowsocks_gateway_shadowsocks_port }}; - } - - server { - listen *:{{ shadowsocks_gateway_server.port }}; - proxy_timeout 5s; - ssl_preread on; - - access_log $access_log_file basic; - error_log ../../..{{ shadowsocks_gateway_tcp_error_log_path }}; - proxy_pass $backend; - } - - server { - listen *:{{ shadowsocks_gateway_server.port }} udp; - - access_log off; - error_log ../../..{{ shadowsocks_gateway_udp_error_log_path }}; - proxy_pass shadowsocks_backend; - } -} diff --git a/roles/shadowsocks-gateway/templates/shadowsocks-gateway.service.j2 b/roles/shadowsocks-gateway/templates/shadowsocks-gateway.service.j2 deleted file mode 100644 index 4c1efc1..0000000 --- a/roles/shadowsocks-gateway/templates/shadowsocks-gateway.service.j2 +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=shadowsocks-gateway -After=shadowsocks.service - -[Service] -User={{ shadowsocks_gateway_user }} -ExecStart=/usr/sbin/nginx -c {{ shadowsocks_gateway_nginx_conf_path }} -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/roles/shadowsocks-gateway/vars/main.yml b/roles/shadowsocks-gateway/vars/main.yml deleted file mode 100644 index 1d5016a..0000000 --- a/roles/shadowsocks-gateway/vars/main.yml +++ /dev/null @@ -1,36 +0,0 @@ -# linux user to run shadowsocks-gateway as -shadowsocks_gateway_user: shadowsocks-gateway -# number of worker_connections in nginx -shadowsocks_gateway_worker_connections: 65536 -# number of worker_rlimit_nofile in nginx -shadowsocks_gateway_worker_rlimit_nofile: 65536 -# port node_exporter to run on -shadowsocks_gateway_node_exporter_port: 47354 -# port shadowsocks-metrics to run on -shadowsocks_gateway_shadowsocks_metrics_port: 27364 -# port shadowsocks to run on locally -shadowsocks_gateway_shadowsocks_port: 52539 - -shadowsocks_gateway_nginx_conf_filename: nginx.conf -shadowsocks_gateway_certificate_filename: cert.pem -shadowsocks_gateway_key_filename: key.pem -shadowsocks_gateway_http_access_log_filename: http_access.log -shadowsocks_gateway_http_error_log_filename: http_error.log -shadowsocks_gateway_tls_access_log_filename: tls_access.log -shadowsocks_gateway_tcp_error_log_filename: tcp_error.log -shadowsocks_gateway_udp_error_log_filename: udp_error.log -shadowsocks_gateway_prometheus_access_log_filename: prometheus_access.log -shadowsocks_gateway_prometheus_error_log_filename: prometheus_error.log -shadowsocks_gateway_pid_filename: nginx.pid - -shadowsocks_gateway_nginx_conf_path: /home/{{ shadowsocks_gateway_user }}/{{shadowsocks_gateway_nginx_conf_filename }} -shadowsocks_gateway_certificate_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_certificate_filename }}" -shadowsocks_gateway_key_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_key_filename }}" -shadowsocks_gateway_http_access_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_http_access_log_filename }}" -shadowsocks_gateway_http_error_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_http_error_log_filename }}" -shadowsocks_gateway_tls_access_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_tls_access_log_filename }}" -shadowsocks_gateway_tcp_error_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_tcp_error_log_filename }}" -shadowsocks_gateway_udp_error_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_udp_error_log_filename }}" -shadowsocks_gateway_prometheus_access_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_prometheus_access_log_filename }}" -shadowsocks_gateway_prometheus_error_log_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_prometheus_error_log_filename }}" -shadowsocks_gateway_pid_path: "/home/{{ shadowsocks_gateway_user }}/{{ shadowsocks_gateway_pid_filename }}" diff --git a/roles/shadowsocks/defaults/main.yml b/roles/shadowsocks/defaults/main.yml deleted file mode 100644 index 2a45160..0000000 --- a/roles/shadowsocks/defaults/main.yml +++ /dev/null @@ -1,12 +0,0 @@ -# port of metrics for prometheus to be exposed -shadowsocks_metrics_port: -# port of shadowsocks behind gateway -shadowsocks_port: -# shadowsocks listen address -shadowsocks_listen_address: -# users as described in /users-example.yml -shadowsocks_users: -# one(!) server item as secribed in /servers-example.yml -shadowsocks_server: -# server uuid as in /servers-example.yml -shadowsocks_server_uuid: diff --git a/roles/shadowsocks/meta/main.yml b/roles/shadowsocks/meta/main.yml deleted file mode 100644 index ca6bf3d..0000000 --- a/roles/shadowsocks/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - role: kernel-optimizations diff --git a/roles/shadowsocks/tasks/main.yml b/roles/shadowsocks/tasks/main.yml deleted file mode 100644 index 5cff6e6..0000000 --- a/roles/shadowsocks/tasks/main.yml +++ /dev/null @@ -1,83 +0,0 @@ -- name: Ensure required variables are defined - assert: - that: - - shadowsocks_port is number - - shadowsocks_listen_address is string - - shadowsocks_metrics_port is number - - shadowsocks_server_uuid is string - -- name: Ensure shadowsocks_users is defined correctly - include_tasks: tasks/assert-users.yml - vars: - users: "{{ shadowsocks_users }}" - -- name: Ensure shadowsocks_server is defined correctly - include_tasks: tasks/assert-server.yml - vars: - server: "{{ shadowsocks_server }}" - -- name: Create user - user: name={{ shadowsocks_user }} - -- name: Get MD5 checksum of outline executable - stat: - path: "/home/{{ shadowsocks_user }}/{{ shadowsocks_executable_name }}" - checksum_algorithm: md5 - register: file_stat - -- name: Detect arch - command: uname -m - check_mode: no - register: arch - -- name: Download & extract outline executable - when: "file_stat.stat.exists == false or file_stat.stat.checksum != shadowsocks_downloads[arch.stdout].md5" - unarchive: - src: "{{ shadowsocks_downloads[arch.stdout].url }}" - dest: "/home/{{ shadowsocks_user }}" - remote_src: yes - extra_opts: - - "{{ shadowsocks_executable_name }}" - register: download - -- name: Set executable ownership, group and permissions - file: - path: "/home/{{ shadowsocks_user }}/{{ shadowsocks_executable_name }}" - group: "{{ shadowsocks_user }}" - owner: "{{ shadowsocks_user }}" - mode: "700" - -- name: Render outline-ss-server config - template: - src: config.yml.j2 - dest: "/home/{{ shadowsocks_user }}/config.yml" - group: "{{ shadowsocks_user }}" - owner: "{{ shadowsocks_user }}" - mode: "600" - register: config - -- name: Remove unexpected files in home - include_tasks: tasks/remove-unexpected-files.yml - vars: - directory: "/home/{{ shadowsocks_user }}" - files: - - config.yml - - "{{ shadowsocks_executable_name }}" - -- name: Render systemd service config - template: - src: shadowsocks.service.j2 - dest: /etc/systemd/system/shadowsocks.service - register: systemd - -- name: Reload daemon - systemd: - daemon_reload: yes - when: systemd.changed - -- name: Restart systemd app service - systemd: - name: shadowsocks.service - state: restarted - enabled: yes - when: systemd.changed or download.changed or config.changed diff --git a/roles/shadowsocks/templates/config.yml.j2 b/roles/shadowsocks/templates/config.yml.j2 deleted file mode 100644 index 9fcff24..0000000 --- a/roles/shadowsocks/templates/config.yml.j2 +++ /dev/null @@ -1,14 +0,0 @@ -# https://github.com/Jigsaw-Code/outline-ss-server/blob/master/cmd/outline-ss-server/config_example.yml - -services: - - listeners: - - type: tcp - address: "{{ shadowsocks_listen_address }}:{{ shadowsocks_port }}" - - type: udp - address: "{{ shadowsocks_listen_address }}:{{ shadowsocks_port }}" - keys: -{% for user in shadowsocks_users | dict2items %} - - cipher: {{ shadowsocks_server.method }} - id: {{ user.value.name }} - secret: {{ (user.key + shadowsocks_server.port | string + shadowsocks_server.method + shadowsocks_server_uuid | string + shadowsocks_server.secret | string) | hash('sha512') }} -{% endfor %} diff --git a/roles/shadowsocks/templates/shadowsocks.service.j2 b/roles/shadowsocks/templates/shadowsocks.service.j2 deleted file mode 100644 index 0389a09..0000000 --- a/roles/shadowsocks/templates/shadowsocks.service.j2 +++ /dev/null @@ -1,12 +0,0 @@ -[Unit] -Description=shadowsocks -After=network.service - -[Service] -User={{ shadowsocks_user }} -AmbientCapabilities=CAP_NET_BIND_SERVICE -ExecStart=/home/{{ shadowsocks_user }}/{{ shadowsocks_executable_name }} -replay_history={{ shadowsocks_replay_history }} -config /home/{{ shadowsocks_user }}/config.yml -metrics 127.0.0.1:{{ shadowsocks_metrics_port }} -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/roles/shadowsocks/vars/main.yml b/roles/shadowsocks/vars/main.yml deleted file mode 100644 index 55a6677..0000000 --- a/roles/shadowsocks/vars/main.yml +++ /dev/null @@ -1,18 +0,0 @@ -# linux user to run shadowsocks server as -shadowsocks_user: shadowsocks -# URL to download outline from -shadowsocks_downloads: - x86_64: - url: https://github.com/Jigsaw-Code/outline-ss-server/releases/download/v1.7.0/outline-ss-server_1.7.0_linux_x86_64.tar.gz - md5: 40710b0e291fc8fa7eafbf5fbbf56035 - arm64: - url: https://github.com/Jigsaw-Code/outline-ss-server/releases/download/v1.7.0/outline-ss-server_1.7.0_linux_arm64.tar.gz - md5: 40710b0e291fc8fa7eafbf5fbbf56035 - armv7l: - url: https://github.com/Jigsaw-Code/outline-ss-server/releases/download/v1.7.0/outline-ss-server_1.7.0_linux_armv7.tar.gz - md5: 37cff757d1fbc2ad1e04e71aa4cdbec7 -# name of outline executable file -shadowsocks_executable_name: outline-ss-server -# replay defense; see https://github.com/Jigsaw-Code/outline-ss-server/blob/master/service/PROBES.md for details -shadowsocks_replay_history: 10000 -# port of metrics for prometheus to be exposed diff --git a/roles/users-configs/defaults/main.yml b/roles/users-configs/defaults/main.yml index f86d46f..5429ac0 100644 --- a/roles/users-configs/defaults/main.yml +++ b/roles/users-configs/defaults/main.yml @@ -9,3 +9,9 @@ users_configs_servers: # relative path of directory where static content should be stored users_configs_static_directory_filename: + +# title users should we when open hiddify +users_configs_title: + +# see https://hiddify.com/app/URL-Scheme +users_configs_support_url: diff --git a/roles/users-configs/files/guide.html b/roles/users-configs/files/guide.html index 50bb884..6760d10 100644 --- a/roles/users-configs/files/guide.html +++ b/roles/users-configs/files/guide.html @@ -19,37 +19,26 @@ if (!parseHash.value) { parseHash.value = window.location.hash.substring(1); // invalidate location so that users cannot share personal links - window.location.hash = '#do-not-share-your-personal-link'; + window.history.pushState({}, '', '#do-not-share-your-personal-link'); } return parseHash.value; } const quit = function () { window.location.href = '/'; - } - - const getSsConfig = function(hash) { - return fetch('/' + hash) - .then(response => response.json()) - .catch(quit); - } - - const getSsConfUri = function(hash) { - return 'ssconf://' + location.host + '/' + hash; - } + }; - const getSsUri = function(server) { - return `ss://${btoa(`${server.method}:${server.password}`)}@${server.server}:${server['server_port']}?prefix=${server['prefix']}`; - } + const getSubscriptionUrl = function(hash) { + return `https://${location.host}/${hash}`; + }; - const getStraisandUri = function(server) { - return 'streisand://import/' + getSsUri(server); + const getHiddifyUrl = function(hash) { + return `hiddify://import/${getSubscriptionUrl(hash)}`; } const render = function(hash) { - const ssConfUri = getSsConfUri(hash); document.getElementById('connect_vpn').addEventListener('click', () => { - window.location.href = ssConfUri; + window.location.href = getHiddifyUrl(hash); }); document.getElementById('body').style = ` display: block; @@ -58,57 +47,11 @@ background-color: white; padding: 1rem; `; - for (element of document.getElementsByClassName('sip008_uri')) { - element.innerHTML = ssConfUri; + const subscriptionUrl = getSubscriptionUrl(hash); + for (element of document.getElementsByClassName('subscription_uri')) { + element.innerHTML = subscriptionUrl; }; - - getSsConfig(hash) - .then(renderStaticConfigurations) - .then(html => document.getElementById('nerd').innerHTML = html); - } - - const renderStaticConfigurations = function(config) { - return ` -
- Конфигурации ниже временные, пока не обновится динамический конфиг. Когда это произойдёт, впн перестанет работать. - В этом случае нужно будет открыть эту страницу и перенастроить клиент с новыми параметрами. Конфиг обновиться может в любой момент. -
- ${config['servers'].map(renderStaticServerConfig).join('')} - `; - }; - - const renderStaticServerConfig = function (server) { - const streisandUri = getStraisandUri(server); - const ssUri = getSsUri(server); - return ` -${ssUri}
- ${server['server']}
${server['server_port']}
${server['password']}
${server['method']}
${server['prefix']}