├── tests ├── inventory └── test.yml ├── vars ├── Debian.yml ├── RedHat.yml └── main.yml ├── handlers └── main.yml ├── tasks ├── main.yml └── RedHat.yml ├── templates ├── shadowsocks.json └── shadowsocks.init ├── .travis.yml ├── defaults └── main.yml ├── meta └── main.yml └── README.md /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost ansible_connection=local 2 | -------------------------------------------------------------------------------- /vars/Debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | shadowsocks_dependencies: 3 | - python-pip 4 | - python-m2crypto 5 | -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | shadowsocks_dependencies: 3 | - python-setuptools 4 | - m2crypto 5 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | shadowsocks_dependencies: 3 | - python-setuptools 4 | - m2crypto 5 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart shadowsocks 3 | service: name=shadowsocks state=restarted 4 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - ansible-shadowsocks 6 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: include OS-specific variables. 3 | include_vars: "{{ item }}" 4 | with_first_found: 5 | - "{{ ansible_distribution }}.yml" 6 | - "{{ ansible_os_family }}.yml" 7 | - "main.yml" 8 | 9 | # Setup/install tasks. 10 | - include: RedHat.yml 11 | when: ansible_os_family == 'RedHat' 12 | -------------------------------------------------------------------------------- /templates/shadowsocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": "{{ ansible_default_ipv4.address }}" 3 | , "server_port": {{ shadowsocks_config_server_port }} 4 | , "local_port": {{ shadowsocks_config_local_port }} 5 | , "password": "{{ shadowsocks_password }}" 6 | , "timeout": {{ shadowsocks_config_timeout }} 7 | , "method": "{{ shadowsocks_config_encryption_method }}" 8 | {% if ansible_kernel | version_compare('3.7', '>=') -%} 9 | , "fast_open": true 10 | {% else -%} 11 | , "fast_open": false 12 | {% endif %} 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | before_install: 6 | - sudo apt-get update -qq 7 | 8 | install: 9 | - pip install ansible 10 | 11 | # Add ansible.cfg to pick up roles path. 12 | - "{ echo '[defaults]'; echo 'roles_path = ../'; } >> ansible.cfg" 13 | 14 | script: 15 | # Check the role/playbook's syntax. 16 | - "ansible-playbook -i tests/inventory tests/test.yml --syntax-check" 17 | 18 | # Run the role/playbook with ansible-playbook. 19 | - "ansible-playbook -i tests/inventory tests/test.yml --sudo" 20 | 21 | # Run the role/playbook again, checking to make sure it's idempotent. 22 | - > 23 | ansible-playbook -i tests/inventory tests/test.yml --sudo 24 | | grep -q 'changed=0.*failed=0' 25 | && (echo 'Idempotence test: pass' && exit 0) 26 | || (echo 'Idempotence test: fail' && exit 1) 27 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | shadowsocks_home: "/etc/shadowsocks" 3 | shadowsocks_password: "apn!proxy!ss!ftw!" 4 | 5 | shadowsocks_config_server_port: "443" 6 | shadowsocks_config_local_port: "3333" 7 | shadowsocks_config_timeout: "600" 8 | shadowsocks_config_encryption_method: "aes-256-cfb" 9 | shadowsocks_config_fast_open: yes 10 | 11 | shadowsocks_sysctl_tweak: yes 12 | shadowsocks_sysctl_params: 13 | - { "key": "fs.file-max", "value": "51200" } 14 | - { "key": "net.core.rmem_max", "value": "67108864" } 15 | - { "key": "net.core.wmem_max", "value": "67108864" } 16 | - { "key": "net.core.rmem_default", "value": "65536" } 17 | - { "key": "net.core.wmem_default", "value": "65536" } 18 | - { "key": "net.core.netdev_max_backlog", "value": "4096" } 19 | - { "key": "net.core.somaxconn", "value": "4096" } 20 | - { "key": "net.ipv4.tcp_syncookies", "value": "1" } 21 | - { "key": "net.ipv4.tcp_tw_reuse", "value": "1" } 22 | - { "key": "net.ipv4.tcp_tw_recycle", "value": "0" } 23 | - { "key": "net.ipv4.tcp_fin_timeout", "value": "30" } 24 | - { "key": "net.ipv4.tcp_keepalive_time", "value": "1200" } 25 | - { "key": "net.ipv4.ip_local_port_range", "value": "10000 65000" } 26 | - { "key": "net.ipv4.tcp_max_syn_backlog", "value": "4096" } 27 | - { "key": "net.ipv4.tcp_max_tw_buckets", "value": "5000" } 28 | - { "key": "net.ipv4.tcp_rmem", "value": "4096 87380 67108864" } 29 | - { "key": "net.ipv4.tcp_wmem", "value": "4096 65536 67108864" } 30 | - { "key": "net.ipv4.tcp_mtu_probing", "value": "1" } 31 | -------------------------------------------------------------------------------- /tasks/RedHat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install dependencies 3 | yum: name={{ shadowsocks_dependencies }} 4 | tags: prepare 5 | 6 | - name: install pip 7 | easy_install: name=pip 8 | tags: prepare 9 | 10 | - name: install Shadowsocks 11 | pip: name=shadowsocks state=latest 12 | notify: restart shadowsocks 13 | tags: install 14 | 15 | - name: assure shadowsocks home dir exists 16 | file: path={{ shadowsocks_home }} state=directory 17 | 18 | - name: generate config file 19 | template: src=shadowsocks.json 20 | dest={{ shadowsocks_home }}/shadowsocks.json 21 | mode=640 22 | owner=root 23 | group=root 24 | force=yes 25 | notify: restart shadowsocks 26 | tags: config 27 | 28 | - name: generate init script 29 | template: src=shadowsocks.init 30 | dest=/etc/init.d/shadowsocks 31 | mode=755 32 | tags: config 33 | 34 | - name: tweak sysctl 35 | sysctl: name="{{ item.key }}" 36 | value="{{ item.value }}" 37 | state=present 38 | reload=yes 39 | ignoreerrors=yes 40 | with_items: shadowsocks_sysctl_params 41 | when: shadowsocks_sysctl_tweak 42 | tags: sysctl 43 | 44 | - name: enable tcp_fastopen if available 45 | sysctl: name="net.ipv4.tcp_fastopen" 46 | value="3" 47 | state=present 48 | reload=yes 49 | when: shadowsocks_sysctl_tweak and ansible_kernel | version_compare('3.7', '>=') 50 | tags: sysctl 51 | 52 | - name: start and enable service 53 | service: name=shadowsocks 54 | state=started 55 | enabled=yes 56 | -------------------------------------------------------------------------------- /templates/shadowsocks.init: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # /etc/init.d/shadowsocks 4 | 5 | ### BEGIN INIT INFO 6 | # Provides: ssserver 7 | # Required-Start: $all 8 | # Required-Stop: $all 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: start and stop shadowsocks server 12 | # Description: shadowsocks is a secured socks5 proxy, designed to protect your internet traffic. 13 | ### END INIT INFO 14 | 15 | # Source function library. 16 | . /etc/rc.d/init.d/functions 17 | 18 | name=shadowsocks 19 | ssserver=/usr/bin/ssserver 20 | conffile={{ shadowsocks_home }}/shadowsocks.json 21 | pidfile=/var/run/$name.pid 22 | lockfile=/var/lock/subsys/$name 23 | 24 | RETVAL=0 25 | 26 | # Check if config exists. 27 | # 28 | if [ ! -f $conffile ] ; then 29 | echo "The configuration file cannot be found!" 30 | exit 0 31 | fi 32 | 33 | start() { 34 | if [ -f $lockfile ]; then 35 | echo "$name is already running!" 36 | exit 0 37 | else 38 | echo "Starting ${name}" 39 | # daemon --pidfile=${pidfile} ${ssserver} -c ${conffile} 40 | # Use ssserver default daemon method 41 | $ssserver -c $conffile -d start 42 | fi 43 | 44 | RETVAL=$? 45 | [ $RETVAL = 0 ] && success 46 | echo 47 | [ $RETVAL = 0 ] && touch $lockfile 48 | return $RETVAL 49 | } 50 | 51 | stop() { 52 | echo -n $"Shutting down ${name}: " 53 | killproc -p ${pidfile} 54 | RETVAL=$? 55 | echo 56 | [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} 57 | } 58 | 59 | # See how we were called. 60 | case "$1" in 61 | start) 62 | start 63 | ;; 64 | stop) 65 | stop 66 | ;; 67 | status) 68 | status $ssserver 69 | RETVAL=$? 70 | ;; 71 | restart) 72 | stop 73 | start 74 | ;; 75 | condrestart|try-restart) 76 | if [ -f $lockfile ]; then 77 | stop 78 | start 79 | fi 80 | RETVAL=$? 81 | ;; 82 | *) 83 | echo $"Usage: $0 {start|stop|restart|condrestart|status}" 84 | RETVAL=2 85 | esac 86 | 87 | exit $RETVAL 88 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Tunghsiao Liu 4 | description: Installs Shadowsocks 5 | company: Sparanoid 6 | license: license (GPLv3) 7 | min_ansible_version: 1.6 8 | platforms: 9 | - name: EL 10 | versions: 11 | # - all 12 | # - 5 13 | - 6 14 | - 7 15 | #- name: GenericUNIX 16 | # versions: 17 | # - all 18 | # - any 19 | #- name: Fedora 20 | # versions: 21 | # - all 22 | # - 16 23 | # - 17 24 | # - 18 25 | # - 19 26 | # - 20 27 | #- name: SmartOS 28 | # versions: 29 | # - all 30 | # - any 31 | #- name: opensuse 32 | # versions: 33 | # - all 34 | # - 12.1 35 | # - 12.2 36 | # - 12.3 37 | # - 13.1 38 | # - 13.2 39 | #- name: Amazon 40 | # versions: 41 | # - all 42 | # - 2013.03 43 | # - 2013.09 44 | #- name: GenericBSD 45 | # versions: 46 | # - all 47 | # - any 48 | #- name: FreeBSD 49 | # versions: 50 | # - all 51 | # - 8.0 52 | # - 8.1 53 | # - 8.2 54 | # - 8.3 55 | # - 8.4 56 | # - 9.0 57 | # - 9.1 58 | # - 9.1 59 | # - 9.2 60 | #- name: Ubuntu 61 | # versions: 62 | # - all 63 | # - lucid 64 | # - maverick 65 | # - natty 66 | # - oneiric 67 | # - precise 68 | # - quantal 69 | # - raring 70 | # - saucy 71 | # - trusty 72 | #- name: SLES 73 | # versions: 74 | # - all 75 | # - 10SP3 76 | # - 10SP4 77 | # - 11 78 | # - 11SP1 79 | # - 11SP2 80 | # - 11SP3 81 | #- name: GenericLinux 82 | # versions: 83 | # - all 84 | # - any 85 | #- name: Debian 86 | # versions: 87 | # - all 88 | # - etch 89 | # - lenny 90 | # - squeeze 91 | # - wheezy 92 | # 93 | # Below are all categories currently available. Just as with 94 | # the platforms above, uncomment those that apply to your role. 95 | # 96 | categories: 97 | #- cloud 98 | #- cloud:ec2 99 | #- cloud:gce 100 | #- cloud:rax 101 | #- clustering 102 | #- database 103 | #- database:nosql 104 | #- database:sql 105 | #- development 106 | #- monitoring 107 | - networking 108 | #- packaging 109 | - system 110 | #- web 111 | dependencies: [] 112 | # List your role dependencies here, one per line. 113 | # Be sure to remove the '[]' above if you add dependencies 114 | # to this list. 115 | 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role: Shadowsocks 2 | 3 | [![Build Status](https://travis-ci.org/sparanoid/ansible-shadowsocks.svg)](https://travis-ci.org/sparanoid/ansible-shadowsocks) 4 | 5 | Install [Shadowsocks](https://github.com/shadowsocks) via Ansible. 6 | 7 | ## Features 8 | 9 | - Install or upgrade Shadowsocks Python version easily 10 | - Tuning `sysctl` automatically for better performance 11 | - Detect `tcp_fastopen` support 12 | - Support init startup script 13 | 14 | ## Requirements 15 | 16 | This role requires Ansible 1.6 or higher and platform requirements are listed in the metadata file. 17 | 18 | ## Dependencies 19 | 20 | None 21 | 22 | ## Example Playbooks 23 | 24 | Install shadowsocks with custom location (default: `/etc/shadowsocks`): 25 | 26 | ```yaml 27 | - hosts: servers 28 | roles: 29 | - { role: sparanoid.shadowsocks, shadowsocks_home: /home/sparanoid/shadowsocks } 30 | ``` 31 | 32 | Install shadowsocks with different encryption (default: `aes-256-cfb`): 33 | 34 | ```yaml 35 | - hosts: servers 36 | roles: 37 | - { role: sparanoid.shadowsocks, shadowsocks_config_encryption_method: salsa20 } 38 | ``` 39 | 40 | Install shadowsocks with different server port (default: `443`): 41 | 42 | ```yaml 43 | - hosts: servers 44 | roles: 45 | - { role: sparanoid.shadowsocks, shadowsocks_config_server_port: 9999 } 46 | ``` 47 | 48 | Install shadowsocks without tuning `sysctl` (default: `shadowsocks_sysctl_tweak: true`): 49 | 50 | ```yaml 51 | - hosts: servers 52 | roles: 53 | - { role: sparanoid.shadowsocks, shadowsocks_sysctl_tweak: false } 54 | ``` 55 | 56 | Install shadowsocks using custom specified password (default: `apn!proxy!ss!ftw!`): 57 | 58 | ```yaml 59 | - hosts: servers 60 | roles: 61 | - { role: sparanoid.shadowsocks, shadowsocks_password: "myFancy@Passwd!" } 62 | ``` 63 | 64 | You can also define password in command line: 65 | 66 | ```shell 67 | $ ansible-playbook shadowsocks-servers.yml --extra-vars "shadowsocks_password=myFancy@Passwd!" 68 | ``` 69 | 70 | ## Todos 71 | 72 | - [x] init.d script 73 | - [x] Tuning `sysctl` support 74 | - [x] Detect `tcp_fastopen` 75 | - [ ] Multiple users support 76 | - [ ] Distro support 77 | - [x] EL 78 | - [ ] Better init.d script for 7 79 | - [ ] Debian 80 | - [ ] Ubuntu 81 | 82 | ## License 83 | 84 | GPLv3 85 | 86 | ## Author Information 87 | 88 | **Tunghsiao Liu** 89 | 90 | - Twitter: @[tunghsiao](http://twitter.com/tunghsiao) 91 | - GitHub: @[sparanoid](http://github.com/sparanoid) 92 | --------------------------------------------------------------------------------