├── README.md ├── ansible.cfg ├── inventory ├── localhost └── selfhost ├── roles ├── certbot │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── cli.ini.j2 ├── nginx │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── centos.yml │ │ ├── debian.yml │ │ └── main.yml │ └── templates │ │ ├── nginx.conf.j2 │ │ ├── proxy.j2 │ │ └── ssl_params.j2 └── v2ray │ ├── defaults │ └── main.yml │ ├── files │ └── v2ray.service │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ └── config.json.j2 └── v2ray.yml /README.md: -------------------------------------------------------------------------------- 1 | # V2Ray for Ansible 2 | 3 | 本 Playbook 将利用 Ansible 在服务器上自动化部署 V2Ray。目前,仅在 Debian 4 | GNU/Linux 10,Centos 7 上测试通过。Debian GNU/Linux 更低版本或 Ubuntu 5 | 系列大概也没有问题,其它 Linux 6 | 发行版本有待进一步测试。使用本仓库请自行承担风险。 7 | 8 | 根据 V2Ray 大多数用户推荐,本 Playbook 采用 [WebSocket+TLS+Web](https://toutyrater.github.io/advanced/wss_and_web.html) 部署方式。换句话说,它将在服务器上安装下列软件: 9 | 10 | * Certbot:用于请求 SSL 证书 11 | * NGINX:用于提供 Web 服务 12 | * V2Ray 13 | 14 | ## 使用要求 15 | 16 | 为了正常使用 V2Ray,应当满足以下要求: 17 | 18 | * 一台云主机,如 [Vultr](https://www.vultr.com/?ref=7123175)、[Linode](https://www.linode.com/?r=28bf53dae49d2c55dd671136769c0b7526db5891)、[DO](https://m.do.co/c/7758457f61ad) 等等。 19 | * 一个域名且已经绑定到云主机。 20 | * Ansible,可参考[官方文档](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-the-control-node)安装。 21 | 22 | ### 使用方法 23 | 24 | 1. 将服务器的 IP 添加到 `inventory/selfhost` 文件: 25 | 26 | $ echo 'your.ip.address' >> inventory/selfhost 27 | 28 | 2. 执行 Playbook 并按提示操作: 29 | 30 | $ ansible-playbook v2ray.yml 31 | 32 | 3. Enjoy. 33 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = inventory 3 | library = library 4 | roles_path = roles 5 | remote_user = root 6 | timeout = 100 7 | forks = 25 8 | poll_interval = 5 9 | gathering = smart 10 | host_key_checking = False 11 | retry_files_enabled = False 12 | ansible_managed = Ansible managed: {file} modified by {uid} on {host} 13 | callback_whitelist = timer, profile_tasks 14 | nocows = 1 15 | 16 | [ssh_connection] 17 | pipelining = True 18 | control_path = /tmp/ansible-ssh-%%h-%%p-%%r 19 | -------------------------------------------------------------------------------- /inventory/localhost: -------------------------------------------------------------------------------- 1 | [local] 2 | 127.0.0.1 ansible_connection=local ansible_python_interpreter=/usr/bin/python2 3 | -------------------------------------------------------------------------------- /inventory/selfhost: -------------------------------------------------------------------------------- 1 | [selfhost] 2 | -------------------------------------------------------------------------------- /roles/certbot/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install package 3 | package: 4 | name: certbot 5 | state: present 6 | 7 | - name: Generate config 8 | template: 9 | src: cli.ini.j2 10 | dest: /etc/letsencrypt/cli.ini 11 | mode: 0644 12 | 13 | - name: Get cert 14 | command: certbot certonly -c /etc/letsencrypt/cli.ini 15 | register: certbot 16 | changed_when: "certbot.rc != 0" 17 | 18 | - name: Add cron 19 | cron: 20 | name: "renew cert" 21 | minute: "52" 22 | hour: "0,12" 23 | user: root 24 | job: "certbot renew -q -c /etc/letsencrypt/cli.ini" 25 | -------------------------------------------------------------------------------- /roles/certbot/templates/cli.ini.j2: -------------------------------------------------------------------------------- 1 | email = {{ email }} 2 | domains = {{ domains | join(', ') }} 3 | rsa-key-size = 4096 4 | noninteractive = True 5 | keep = True 6 | expand = True 7 | agree-tos = True 8 | pre-hook = systemctl stop nginx 9 | post-hook = systemctl start nginx 10 | authenticator = standalone 11 | #installer = nginx 12 | #preferred-challenges = tls-sni-01 13 | -------------------------------------------------------------------------------- /roles/nginx/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | srv_name: default 3 | enabled_https: True 4 | enabled_websocket: True 5 | add_location: False 6 | -------------------------------------------------------------------------------- /roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart nginx 3 | service: 4 | name: nginx 5 | state: restarted 6 | -------------------------------------------------------------------------------- /roles/nginx/tasks/centos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Add rpm key 4 | rpm_key: 5 | key: http://nginx.org/keys/nginx_signing.key 6 | state: present 7 | 8 | - name: Add rpm repository 9 | yum_repository: 10 | name: nginx 11 | baseurl: https://nginx.org/packages/mainline/centos/{{ ansible_distribution_major_version|int }}/$basearch/ 12 | description: NGINX Repository 13 | enabled: yes 14 | gpgcheck: yes 15 | 16 | - name: Set nginx user 17 | set_fact: 18 | nginx_user: nginx -------------------------------------------------------------------------------- /roles/nginx/tasks/debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Get apt key 3 | apt_key: 4 | url: http://nginx.org/keys/nginx_signing.key 5 | state: present 6 | 7 | - name: Add apt repository 8 | apt_repository: 9 | repo: "{{ item }}" 10 | state: present 11 | filename: nginx 12 | loop: 13 | - deb https://nginx.org/packages/mainline/{{ ansible_distribution|lower }}/ {{ ansible_distribution_release }} nginx 14 | - deb-src https://nginx.org/packages/mainline/{{ ansible_distribution|lower }}/ {{ ansible_distribution_release }} nginx 15 | 16 | - name: Set nginx user 17 | set_fact: 18 | nginx_user: www-data 19 | -------------------------------------------------------------------------------- /roles/nginx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - import_tasks: debian.yml 3 | when: ansible_os_family == "Debian" 4 | 5 | - import_tasks: centos.yml 6 | when: ansible_distribution == "CentOS" 7 | 8 | - name: Install package 9 | package: 10 | name: nginx 11 | state: present 12 | 13 | - name: Enable service 14 | systemd: 15 | name: nginx 16 | state: started 17 | daemon_reload: yes 18 | enabled: yes 19 | 20 | - name: Generate config 21 | template: 22 | src: "{{ item }}.j2" 23 | dest: "/etc/nginx/{{ item }}" 24 | mode: 0644 25 | loop: 26 | - nginx.conf 27 | - ssl_params 28 | notify: restart nginx 29 | 30 | - name: Ensure directory exist 31 | file: 32 | path: "/etc/nginx/sites-{{ item }}" 33 | state: directory 34 | loop: 35 | - available 36 | - enabled 37 | 38 | - name: Add virtual host 39 | template: 40 | src: "{{ item.key }}.j2" 41 | dest: "/etc/nginx/sites-available/{{ item.value.to }}" 42 | mode: 0644 43 | with_dict: "{{ domains }}" 44 | 45 | - name: Enable virtual host 46 | file: 47 | src: "/etc/nginx/sites-available/{{ item.value.to }}" 48 | dest: "/etc/nginx/sites-enabled/{{ item.value.to }}" 49 | state: link 50 | with_dict: "{{ domains}}" 51 | notify: restart nginx 52 | 53 | - name: Remove default virtual host 54 | file: 55 | path: "/etc/nginx/{{ item }}" 56 | state: absent 57 | loop: 58 | - sites-enabled/default 59 | - conf.d/default.conf 60 | notify: restart nginx 61 | 62 | - name: Create ACME-challenge common directory 63 | file: 64 | name: /var/www/_letsencrypt 65 | owner: "{{ nginx_user }}" 66 | group: "{{ nginx_user }}" 67 | mode: 0755 68 | state: directory 69 | 70 | - name: Create install directory 71 | file: 72 | name: "{{ install_directory }}" 73 | owner: "{{ nginx_user }}" 74 | group: "{{ nginx_user }}" 75 | state: directory 76 | when: install_directory is defined 77 | -------------------------------------------------------------------------------- /roles/nginx/templates/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | {% if ansible_os_family == "Debian" %} 2 | user www-data; 3 | {% endif %} 4 | {% if ansible_distribution == "CentOS" %} 5 | user root; 6 | {% endif %} 7 | worker_processes auto; 8 | pid /run/nginx.pid; 9 | 10 | events { 11 | use epoll; 12 | worker_connections 1024; 13 | } 14 | 15 | http { 16 | sendfile on; 17 | tcp_nopush on; 18 | tcp_nodelay on; 19 | server_tokens off; 20 | keepalive_timeout 65; 21 | types_hash_max_size 2048; 22 | server_names_hash_bucket_size 64; 23 | client_max_body_size 512m; 24 | 25 | # Enable asynchronous file I/O 26 | aio threads; 27 | 28 | include /etc/nginx/mime.types; 29 | default_type application/octet-stream; 30 | 31 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE 32 | ssl_prefer_server_ciphers on; 33 | 34 | access_log /var/log/nginx/access.log; 35 | error_log /var/log/nginx/error.log; 36 | 37 | gzip on; 38 | gzip_disable "msie6"; 39 | 40 | gzip_vary on; 41 | gzip_proxied any; 42 | gzip_comp_level 6; 43 | gzip_buffers 16 8k; 44 | gzip_http_version 1.1; 45 | gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; 46 | 47 | include /etc/nginx/conf.d/*.conf; 48 | include /etc/nginx/sites-enabled/*; 49 | } 50 | -------------------------------------------------------------------------------- /roles/nginx/templates/proxy.j2: -------------------------------------------------------------------------------- 1 | upstream {{ srv_name }}_backend { 2 | server {{ domains.proxy.from }}; 3 | 4 | keepalive 128; 5 | } 6 | 7 | server { 8 | listen 80; 9 | server_name {{ domains.proxy.to }}; 10 | {% if enabled_https %} 11 | return 301 https://$host$request_uri; 12 | } 13 | 14 | server { 15 | listen 443 ssl http2; 16 | server_name {{ domains.proxy.to }}; 17 | 18 | ssl_certificate /etc/letsencrypt/live/{{ domains.proxy.to }}/fullchain.pem; 19 | ssl_certificate_key /etc/letsencrypt/live/{{ domains.proxy.to }}/privkey.pem; 20 | ssl_trusted_certificate /etc/letsencrypt/live/{{ domains.proxy.to }}/fullchain.pem; 21 | include /etc/nginx/ssl_params; 22 | {% endif %} 23 | 24 | access_log /var/log/nginx/{{ domains.proxy.to }}_access.log; 25 | error_log /var/log/nginx/{{ domains.proxy.to }}_error.log warn; 26 | 27 | location / { 28 | proxy_pass http://{{ srv_name }}_backend; 29 | proxy_http_version 1.1; 30 | proxy_redirect off; 31 | 32 | proxy_set_header Host $host; 33 | proxy_set_header X-Real-IP $remote_addr; 34 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 35 | proxy_set_header X-Forwarded-Proto $scheme; 36 | proxy_set_header X-Forwarded-Host $host; 37 | proxy_set_header X-Forwarded-Port $server_port; 38 | proxy_set_header X-Nginx-Proxy true; 39 | 40 | {% if enabled_websocket %} 41 | # For WebSocket 42 | proxy_set_header Upgrade $http_upgrade; 43 | proxy_set_header Connection "Upgrade"; 44 | {% else %} 45 | proxy_set_header Connection ""; 46 | {% endif %} 47 | } 48 | 49 | {% if add_location %} 50 | {{ add_location }} 51 | {% endif %} 52 | } 53 | -------------------------------------------------------------------------------- /roles/nginx/templates/ssl_params.j2: -------------------------------------------------------------------------------- 1 | # See also: https://mozilla.github.io/server-side-tls/ssl-config-generator/ 2 | 3 | ssl_session_timeout 1d; 4 | ssl_session_cache shared:SSL:50m; 5 | ssl_session_tickets off; 6 | 7 | # Modern configuration 8 | ssl_protocols TLSv1.2; 9 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; 10 | ssl_prefer_server_ciphers on; 11 | 12 | # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) 13 | add_header Strict-Transport-Security max-age=15768000; 14 | 15 | # OCSP Stapling 16 | # fetch OCSP records from URL in ssl_certificate and cache them 17 | ssl_stapling on; 18 | ssl_stapling_verify on; 19 | -------------------------------------------------------------------------------- /roles/v2ray/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | app_ver: "4.32.1" 3 | app_arch: "64" 4 | app_port: 8600 5 | -------------------------------------------------------------------------------- /roles/v2ray/files/v2ray.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=V2Ray Service 3 | After=network.target 4 | Wants=network.target 5 | 6 | [Service] 7 | # This service runs as root. You may consider to run it as another user for security concerns. 8 | # By uncommenting the following two lines, this service will run as user v2ray/v2ray. 9 | # More discussion at https://github.com/v2ray/v2ray-core/issues/1011 10 | # User=v2ray 11 | # Group=v2ray 12 | Type=simple 13 | PIDFile=/run/v2ray.pid 14 | ExecStart=/usr/bin/v2ray -config /etc/v2ray/config.json 15 | Restart=on-failure 16 | # Don't restart in the case of configuration error 17 | RestartPreventExitStatus=23 18 | Environment=V2RAY_LOCATION_ASSET=/opt/v2ray 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /roles/v2ray/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart v2ray 3 | systemd: 4 | name: v2ray 5 | state: restarted 6 | -------------------------------------------------------------------------------- /roles/v2ray/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ensure directory exist 3 | file: 4 | path: "{{ item }}" 5 | state: directory 6 | loop: 7 | - /opt/v2ray 8 | - /etc/v2ray 9 | 10 | - name: get v2ray binary package 11 | unarchive: 12 | src: "https://github.com/v2fly/v2ray-core/releases/download/v{{ app_ver }}/v2ray-linux-{{ app_arch }}.zip" 13 | dest: /opt/v2ray 14 | remote_src: yes 15 | 16 | - name: symlink binary files 17 | file: 18 | src: "/opt/v2ray/{{ item }}" 19 | dest: "/usr/bin/{{ item }}" 20 | state: link 21 | loop: 22 | - v2ray 23 | - v2ctl 24 | 25 | - name: get uuid 26 | command: v2ctl uuid 27 | register: uuid 28 | changed_when: "uuid.rc != 0" 29 | 30 | - name: generate v2ray config 31 | template: 32 | src: config.json.j2 33 | dest: /etc/v2ray/config.json 34 | notify: 35 | - restart v2ray 36 | 37 | - name: copy systemd config 38 | copy: 39 | src: v2ray.service 40 | dest: /etc/systemd/system/v2ray.service 41 | 42 | - name: enable v2ray service 43 | systemd: 44 | name: v2ray 45 | state: started 46 | daemon_reload: yes 47 | enabled: yes 48 | -------------------------------------------------------------------------------- /roles/v2ray/templates/config.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "inbounds": [ 3 | { 4 | "port": {{ app_port }}, 5 | "listen": "127.0.0.1", 6 | "protocol": "vmess", 7 | "settings": { 8 | "clients": [ 9 | { 10 | "id": "{{ uuid.stdout }}", 11 | "alterId": 64 12 | } 13 | ] 14 | }, 15 | "streamSettings": { 16 | "network": "ws", 17 | "wsSettings": { 18 | "path": "/" 19 | }, 20 | "sockopt": { 21 | "tcpFastOpen": true 22 | } 23 | } 24 | } 25 | ], 26 | "outbounds": [ 27 | { 28 | "protocol": "freedom", 29 | "settings": {} 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /v2ray.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: V2ray 3 | hosts: selfhost 4 | gather_facts: true 5 | become: true 6 | 7 | vars: 8 | proxy_port: 8600 9 | proxy_from: "127.0.0.1:{{ proxy_port }}" 10 | 11 | vars_prompt: 12 | - name: email_addr 13 | prompt: "Email" 14 | default: "your.email@example.com" 15 | private: no 16 | - name: domain_name 17 | prompt: "Domain name" 18 | default: "your.domain.example.com" 19 | private: no 20 | - name: v2ray_ver 21 | prompt: "V2Ray version" 22 | default: "4.32.1" 23 | private: no 24 | - name: v2ray_arch 25 | prompt: "V2Ray arch" 26 | default: "64" 27 | private: no 28 | 29 | roles: 30 | - role: certbot 31 | email: "{{ email_addr }}" 32 | domains: 33 | - "{{ domain_name }}" 34 | - role: nginx 35 | domains: 36 | proxy: 37 | to: "{{ domain_name }}" 38 | from: "{{ proxy_from }}" 39 | srv_name: "v2ray" 40 | - role: v2ray 41 | app_ver: "{{ v2ray_ver }}" 42 | app_arch: "{{ v2ray_arch }}" 43 | app_port: "{{ proxy_port }}" 44 | --------------------------------------------------------------------------------