├── .gitignore ├── LICENSE ├── README.md ├── ansible.cfg ├── group_vars └── all ├── roles ├── cjdns │ ├── files │ │ ├── cjdcmd-git.PKGBUILD │ │ └── cjdns-git.PKGBUILD │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── cjdcmd.yml │ │ ├── cjdns.yml │ │ └── main.yml │ └── templates │ │ └── update-cjdns.j2 └── common │ ├── handlers │ └── main.yml │ ├── tasks │ ├── iptables.yml │ ├── main.yml │ ├── mirrorlist.yml │ ├── ntp.yml │ ├── openssh.yml │ ├── sysctl.yml │ ├── users.yml │ └── vnstat.yml │ └── templates │ ├── ip6tables.rules.j2 │ ├── iptables.rules.j2 │ ├── ntp.conf.j2 │ ├── sshd_config.j2 │ └── sudoers.j2 └── site.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | Vagrantfile 3 | hosts 4 | roles/cjdns/files/*-cjdroute.conf 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2014, Aaron Bull Schaefer 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hyperboriarch 2 | ============= 3 | 4 | Hyperboriarch is a set of [Ansible](http://www.ansibleworks.com/) 5 | playbooks for installing, configuring, and maintaining 6 | [Project Meshnet](https://projectmeshnet.org/)'s 7 | [cjdns](https://github.com/cjdelisle/cjdns#readme) routing software on 8 | [Arch Linux](https://www.archlinux.org/) hosts. It can also perform the 9 | basic tasks necessary to safeguard a freshly installed server, so you can 10 | go from nothing to a relatively secure cjdns node in no time flat. Welcome 11 | to [Hyperboria](http://hyperboria.net/)! 12 | 13 | > If you're new to the project and looking to get connected to the 14 | > network, first you'll need to 15 | > [find a peer](https://ezcrypt.it/7d7n#BmTgRe7XkKFbhhS9lGUsxUUb). 16 | 17 | Overview 18 | -------- 19 | 20 | If you apply the entire _site.yml_ playbook to your hosts, the following 21 | tasks will be handled for you by the "common" role: 22 | 23 | * an updated pacman mirrorlist will be downloaded 24 | * an non-root administrative user will be created 25 | * OpenSSH will be configured more securely 26 | * basic ingress firewall rules will be put into place 27 | * kernel parameters will be set to harden the network stack 28 | * the Network Time Protocol (NTP) service will be configured 29 | * vnStat will be installed to monitor network traffic usage 30 | 31 | ...and the following tasks will be handled by the "cjdns" role: 32 | 33 | * cjdns and (optionally) [cjdcmd](https://github.com/inhies/cjdcmd#readme) 34 | will be built and installed from the latest upstream git revision 35 | * a host specific _cjdroute.conf_ file will be copied over from the 36 | controlling machine 37 | 38 | There are a lot of little conveniences automatically handled for you as 39 | well. For instance, the mirrorlist will only be updated on the initial 40 | run, the firewall will open up your cjdns port if it finds a valid 41 | _cdjroute.conf_ file, cdjcmd will generate its required _~/.cjdadmin_ 42 | file, etc.. 43 | 44 | _Note that you don't have to apply all of these tasks; everything is 45 | [tagged](http://www.ansibleworks.com/docs/playbooks2.html#tags) for 46 | flexibility._ 47 | 48 | Initial Setup 49 | ------------- 50 | 51 | $ git clone https://github.com/elasticdog/hyperboriarch.git 52 | $ cd hyperboriarch/ 53 | 54 | ### Prerequisites 55 | 56 | * Ansible v1.3+ 57 | * Arch Linux host(s) 58 | * root-level access on the host(s), directly or via sudo 59 | * an [inventory file](http://www.ansibleworks.com/docs/patterns.html) 60 | 61 | For the inventory hosts file, it's easiest to keep it in the same 62 | directory as Hyperboriarch so Ansible can find the correct *group_vars*. 63 | By default, things are configured to look for a file named "hosts", but 64 | you can override that either by setting the `ANSIBLE_HOSTS` environment 65 | variable to point to its full path, or use the `-i ` flag 66 | on all of your Ansible commands. 67 | 68 | ### Bootstrapping 69 | 70 | Arch Linux doesn’t have Python v2 installed by default, which is 71 | a dependency for Ansible. Luckily we can use the 72 | [raw module](http://ansibleworks.com/docs/modules.html#raw) to fix that: 73 | 74 | > For the bootstrapping instructions, I'm assuming that you can connect to 75 | > your hosts as the _root_ user...adjust these commands as necessary. 76 | 77 | $ ansible all -m raw -a '/usr/bin/pacman -Sy --noconfirm python2' --user=root 78 | 79 | Always having to specify the user can get annoying, and using _root_ 80 | directly isn't the most secure practice. Instead we can create a new 81 | administrative user based on your current username and 82 | [ssh key](https://wiki.archlinux.org/index.php/SSH_keys) by running just 83 | the "bootstrap" tagged tasks from the playbook: 84 | 85 | $ ansible-playbook site.yml --tags=bootstrap --user=root 86 | 87 | Now you should be able to connect as yourself for all future tasks: 88 | 89 | $ ansible all -m ping 90 | 91 | ### cjdroute.conf 92 | 93 | The last thing we need to worry about is the _/etc/cjdroute.conf_ file 94 | that cjdns needs in order to run. It should be unique per host and 95 | contains your cryptographic key pair and network peering data. 96 | 97 | ##### Use an Existing Config 98 | 99 | If you already have a _cjdroute.conf_ file, simply copy it to the expected 100 | location on the controlling machine and Ansible will push it to the proper 101 | host: 102 | 103 | $ cp /cjdroute.conf roles/cjdns/files/{{ inventory_hostname }}-cjdroute.conf 104 | 105 | As a concrete example, let's say you're managing a host 106 | "alice.example.com", and you have a _cjdroute.conf_ file for it in your 107 | home directory: 108 | 109 | $ cp ~/cjdroute.conf roles/cjdns/files/alice.example.com-cjdroute.conf 110 | $ ansible-playbook site.yml 111 | 112 | ##### Generate a New Config 113 | 114 | If you're starting from scratch, you can generate a config with the 115 | `cjdroute` command on the managed host(s) _after_ you've done your first 116 | playbook run (expect a failure): 117 | 118 | $ ansible-playbook site.yml 119 | ... 120 | FATAL: all hosts have already failed -- aborting 121 | ... 122 | $ ansible all -m shell -a "[[ -f /etc/cjdroute.conf ]] || /usr/bin/cjdroute --genconf > /tmp/cjdroute.conf" 123 | $ ansible all -m fetch -a "src=/tmp/cjdroute.conf dest=roles/cjdns/files/{{ inventory_hostname }}-cjdroute.conf flat=yes" 124 | $ ansible-playbook site.yml 125 | 126 | ### Firewall Considerations 127 | 128 | At this point, cjdns should be up and running on your host(s), but it will 129 | still have incoming connections blocked by the firewall. Now that we've 130 | pushed a _cjdroute.conf_ file, Hyperboriarch can determine which port 131 | needs to be opened up. Run the "iptables" tagged tasks one final time to 132 | make the change: 133 | 134 | $ ansible-playbook site.yml --tags=iptables 135 | 136 | Only cjdns and ssh (TCP port 22) are allowed through the firewall out of 137 | the box, so you'll have to update the appropriate templates if you want to 138 | expose other services. 139 | 140 | Updating cjdns 141 | -------------- 142 | 143 | The cjdns project is alpha software and still under heavy development, so 144 | it's best to keep up-to-date with all of the upstream changes. You can 145 | easily rebuild the latest version and upgrade your systems by running the 146 | "cjdns" tagged tasks: 147 | 148 | $ ansible-playbook site.yml --tags=cjdns 149 | 150 | Playbook Options 151 | ---------------- 152 | 153 | Most of the tasks performed by Hyperboriarch can be customized by setting 154 | the value of particular variables. The default values for these variables 155 | are set and documented in the `group_vars/all` file on the controlling 156 | machine. 157 | 158 | If you just want to override the values for a single run, you can use the 159 | `--extra-vars` flag: 160 | 161 | $ ansible-playbook site.yml --extra-vars="update_mirrorlist=true" 162 | 163 | License 164 | ------- 165 | 166 | Hyperboriarch is provided under the terms of the 167 | [ISC License](https://en.wikipedia.org/wiki/ISC_license). 168 | 169 | Copyright © 2013–2014, [Aaron Bull Schaefer](mailto:aaron@elasticdog.com). 170 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | hostfile = hosts 3 | 4 | [ssh_connection] 5 | pipelining = true 6 | -------------------------------------------------------------------------------- /group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables listed here are applicable to all host groups. 3 | # The defaults should be sane for most setups, but can be adjusted below. 4 | 5 | # the administrative username/uid to create on the remote box(es) 6 | admin_user: "{{ lookup('env','USER') }}" 7 | admin_uid: 1111 8 | 9 | # country code for downloading a geographically local mirrorlist 10 | # 11 | # AU AT BY BE BR BG CA CL CN CO CZ DK EE FI FR DE GR HK HU IS IN 12 | # ID IE IL IT JP KZ KR LV LU MK NL NC NZ NO PL PT RO RU RS SG SK 13 | # ZA ES LK SE CH TW TR UA GB US UZ VN all 14 | country_code: "US" 15 | 16 | # whether or not to install the cjdcmd tool 17 | install_cjdcmd: true 18 | 19 | # sysctl runtime kernel parameters 20 | net_ipv4_conf_all_rp_filter: 1 21 | net_ipv4_tcp_rfc1337: 1 22 | net_ipv4_tcp_tw_reuse: 1 23 | 24 | # the network time protocol servers to utilize 25 | ntp_servers: 26 | - "0.us.pool.ntp.org" 27 | - "1.us.pool.ntp.org" 28 | - "2.us.pool.ntp.org" 29 | - "3.us.pool.ntp.org" 30 | 31 | # where on the remote machine to build cjdns packages 32 | package_build_dir: "/usr/local/src" 33 | 34 | # the host's ip address(es) to bind ssh to; 35 | # ideally this should be your public ipv4 address 36 | sshd_listenaddress: "{{ ansible_all_ipv4_addresses }}" 37 | 38 | # the path on the *controlling* machine to the user's ssh pubkey 39 | ssh_pubkey: "{{ lookup('env','HOME') }}/.ssh/id_rsa.pub" 40 | 41 | # whether or not to force a mirrorlist update on every run 42 | update_mirrorlist: false 43 | 44 | # whether or not to force a package cache update on every run 45 | update_pkgcache: false 46 | 47 | 48 | # *** DO NOT EDIT BELOW THIS LINE *** 49 | ansible_python_interpreter: /usr/bin/python2 50 | -------------------------------------------------------------------------------- /roles/cjdns/files/cjdcmd-git.PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Aaron Bull Schaefer 2 | # Contributor: Enric Morales 3 | 4 | pkgname=cjdcmd-git 5 | _gitname=cjdcmd 6 | pkgver=0.0 7 | pkgrel=1 8 | pkgdesc='a command line tool for interacting with cjdns' 9 | arch=('i686' 'x86_64' 'armv6h') 10 | url="https://github.com/inhies/${_gitname}" 11 | license=('GPL3') 12 | makedepends=('mercurial' 'git' 'go') 13 | provides=("${_gitname}") 14 | conflicts=("${_gitname}") 15 | source=("git://github.com/inhies/${_gitname}.git#branch=master") 16 | sha256sums=('SKIP') 17 | _gourl=github.com/inhies/${_gitname} 18 | 19 | pkgver() { 20 | cd "${_gitname}" 21 | printf '0.%s.%s' "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 22 | } 23 | 24 | build() { 25 | cd "${srcdir}" 26 | 27 | GOPATH="${srcdir}" go get -fix -v -x ${_gourl} 28 | } 29 | 30 | package() { 31 | cd "${srcdir}" 32 | 33 | install -D -m755 "bin/${_gitname}" "${pkgdir}/usr/bin/${_gitname}" 34 | } 35 | -------------------------------------------------------------------------------- /roles/cjdns/files/cjdns-git.PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Aaron Bull Schaefer 2 | # Contributor: Prurigro 3 | # Contributor: Werecat 4 | # Contributor: Xyne 5 | 6 | pkgname=cjdns-git 7 | _gitname=cjdns 8 | pkgver=0.0 9 | pkgrel=1 10 | pkgdesc='A routing engine designed for security, scalability, speed and ease of use.' 11 | arch=('i686' 'x86_64' 'armv6h') 12 | url="https://github.com/cjdelisle/${_gitname}" 13 | license=('GPL3') 14 | makedepends=('git' 'nodejs') 15 | optdepends=('libnacl: speed up the build process by skipping the need to compile cnacl') 16 | provides=("${_gitname}") 17 | conflicts=("${_gitname}") 18 | options=('!buildflags' '!makeflags') 19 | source=("git://github.com/cjdelisle/${_gitname}.git#branch=master") 20 | sha256sums=('SKIP') 21 | 22 | pkgver() { 23 | cd "${_gitname}" 24 | printf '0.%s.%s' "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 25 | } 26 | 27 | prepare() { 28 | if [[ ! -L "${srcdir}/pybin/python" ]]; then 29 | install -d "${srcdir}/pybin" 30 | ln -s /usr/bin/python2 "${srcdir}/pybin/python" 31 | fi 32 | } 33 | 34 | build() { 35 | cd "${srcdir}/${_gitname}" 36 | PATH="${srcdir}/pybin:$PATH" NO_DEBUG=1 ./do 37 | } 38 | 39 | package() { 40 | cd "${srcdir}/${_gitname}" 41 | install -D -m755 cjdroute "${pkgdir}/usr/bin/cjdroute" 42 | install -D -m644 "contrib/systemd/${_gitname}.service" "${pkgdir}/usr/lib/systemd/system/${_gitname}.service" 43 | } 44 | -------------------------------------------------------------------------------- /roles/cjdns/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook handles cjdns notifications. 3 | 4 | - name: restart the cjdns daemon 5 | service: name=cjdns state=restarted 6 | 7 | - name: clean up older cjdns-related packages 8 | command: find "{{ package_build_dir }}" -maxdepth 2 -type f -name "cjd*-git-*.pkg.tar.xz" -mtime +28 -print -delete 9 | register: clean_up_cjdns_packages 10 | changed_when: clean_up_cjdns_packages.stdout.find('pkg.tar.xz') != -1 11 | 12 | - name: copy the cjdcmd config file to the admin user's home directory 13 | command: install --preserve-timestamps --owner="{{ admin_user }}" --group=users --mode=0600 /root/.cjdnsadmin "/home/{{ admin_user }}/.cjdnsadmin" 14 | -------------------------------------------------------------------------------- /roles/cjdns/tasks/cjdcmd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook contains plays to install and maintain cjdcmd. 3 | 4 | - name: create the cjdcmd build directory 5 | file: state=directory path="{{ package_build_dir }}/cjdcmd-git" 6 | owner=root group=users mode=0775 7 | 8 | - name: create the cjdcmd pkgbuild file 9 | copy: src=cjdcmd-git.PKGBUILD dest="{{ package_build_dir }}/cjdcmd-git/PKGBUILD" 10 | owner=root group=users mode=0664 11 | 12 | - name: build and install cjdcmd from git 13 | command: /usr/local/bin/update-cjdns cjdcmd-git 14 | register: update_cjdcmd 15 | changed_when: "update_cjdcmd.stderr.find('WARNING: A package has already been built') == -1" 16 | notify: clean up older cjdns-related packages 17 | 18 | - name: configure the cjdcmd tool 19 | command: /usr/bin/cjdcmd cjdnsadmin --file /etc/cjdroute.conf creates=/root/.cjdnsadmin 20 | notify: copy the cjdcmd config file to the admin user's home directory 21 | -------------------------------------------------------------------------------- /roles/cjdns/tasks/cjdns.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook contains plays to install and maintain cjdns. 3 | 4 | - name: create the cjdns build directory 5 | file: state=directory path="{{ package_build_dir }}/cjdns-git" 6 | owner=root group=users mode=0775 7 | 8 | - name: create the cjdns pkgbuild file 9 | copy: src=cjdns-git.PKGBUILD dest="{{ package_build_dir }}/cjdns-git/PKGBUILD" 10 | owner=root group=users mode=0664 11 | 12 | - name: create the update-cjdns script 13 | template: src=update-cjdns.j2 dest=/usr/local/bin/update-cjdns 14 | owner=root group=root mode=0755 15 | tags: cjdcmd 16 | 17 | - name: build and install cjdns from git 18 | command: /usr/local/bin/update-cjdns 19 | register: update_cjdns 20 | changed_when: "update_cjdns.stderr.find('WARNING: A package has already been built') == -1" 21 | notify: 22 | - restart the cjdns daemon 23 | - clean up older cjdns-related packages 24 | 25 | - name: configure the cjdns daemon 26 | copy: src="{{ inventory_hostname }}-cjdroute.conf" dest=/etc/cjdroute.conf 27 | owner=root group=root mode=0600 28 | notify: restart the cjdns daemon 29 | tags: addpeer 30 | 31 | - name: start the cjdns daemon and enable it on boot 32 | service: name=cjdns state=started enabled=true 33 | -------------------------------------------------------------------------------- /roles/cjdns/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook contains plays to configure cjdns-related software. 3 | 4 | - include: cjdns.yml 5 | - include: cjdcmd.yml tags=cjdcmd 6 | when: install_cjdcmd 7 | -------------------------------------------------------------------------------- /roles/cjdns/templates/update-cjdns.j2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # 4 | # update-cjdns 5 | # 6 | # Meant to run periodically in order to download, build, package, and install 7 | # the latest cjdns revision from the upstream git repository. The script 8 | # optionally takes an argument for a different package to build, assuming 9 | # there's a PKGBUILD for it in the corresponding $BUILD_DIR. 10 | # 11 | 12 | package=$1 13 | 14 | readonly BUILD_DIR="{{ package_build_dir }}/${package:=cjdns-git}" 15 | readonly BASENAME="${0##*/}" # name of this script for error output 16 | 17 | # print the specified message and exit with the given code, if one was supplied 18 | error_exit() { 19 | echo "${BASENAME} (ERROR) - ${1}" 1>&2 20 | exit 1 21 | } 22 | 23 | [[ -d $BUILD_DIR ]] || error_exit "build directory ${BUILD_DIR} does not exist" 24 | 25 | cd "${BUILD_DIR}" 26 | /usr/bin/sudo -su {{ admin_user }} /usr/bin/makepkg --clean --syncdeps --install --noconfirm 27 | 28 | [[ $? -eq 0 ]] || error_exit 'makepkg build failed' 29 | 30 | exit 0 31 | -------------------------------------------------------------------------------- /roles/common/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook handles notifications common to all nodes. 3 | 4 | - name: restore the iptables rules 5 | service: name=iptables state=restarted 6 | 7 | - name: restore the ip6tables rules 8 | service: name=ip6tables state=restarted 9 | 10 | - name: restart the ntp daemon 11 | service: name=ntpd state=restarted 12 | 13 | - name: restart the openssh daemon 14 | service: name=sshd state=restarted 15 | -------------------------------------------------------------------------------- /roles/common/tasks/iptables.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook configures the firewall based on stateful packet filtering. 3 | 4 | - name: check for existence of /etc/cjdroute.conf 5 | stat: path=/etc/cjdroute.conf 6 | register: cjdroute_conf 7 | 8 | # due to purposeful separation of the common and cjdns roles, this will only 9 | # work after /etc/cjdroute.conf exists (i.e., not on the initial playbook run) 10 | - name: parse /etc/cjdroute.conf for the cjdns bind port 11 | shell: /usr/bin/cpp -P /etc/cjdroute.conf | /usr/bin/python2 -c 'import json,sys; obj=json.load(sys.stdin); print obj["interfaces"]["UDPInterface"][0]["bind"].split(":")[1]' 12 | when: cjdroute_conf.stat.exists 13 | register: cjdns_port 14 | changed_when: false 15 | 16 | - name: configure iptables packet filtering 17 | template: src=iptables.rules.j2 dest=/etc/iptables/iptables.rules 18 | owner=root group=root mode=0644 19 | notify: restore the iptables rules 20 | 21 | - name: ensure the iptables rules are restored on boot 22 | service: name=iptables state=started enabled=true 23 | 24 | - name: configure ip6tables packet filtering 25 | template: src=ip6tables.rules.j2 dest=/etc/iptables/ip6tables.rules 26 | owner=root group=root mode=0644 27 | notify: restore the ip6tables rules 28 | 29 | - name: ensure the ip6tables rules are restored on boot 30 | service: name=ip6tables state=started enabled=true 31 | -------------------------------------------------------------------------------- /roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook contains common plays that will be run on all nodes. 3 | 4 | - include: mirrorlist.yml tags=mirrorlist 5 | - include: users.yml tags=users 6 | - include: openssh.yml tags=ssh 7 | - include: iptables.yml tags=iptables 8 | - include: sysctl.yml tags=sysctl 9 | - include: ntp.yml tags=ntp 10 | - include: vnstat.yml tags=vnstat 11 | -------------------------------------------------------------------------------- /roles/common/tasks/mirrorlist.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook downloads a current mirrorlist for pacman. 3 | 4 | - name: check for existence of an old mirrorlist 5 | stat: path=/etc/pacman.d/mirrorlist.old 6 | register: mirrorlist_old 7 | tags: pkgcache 8 | 9 | - name: make a backup of the original mirrorlist 10 | command: cp -p /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.old 11 | when: update_mirrorlist or not mirrorlist_old.stat.exists 12 | 13 | - name: download a current mirrorlist 14 | get_url: url=https://www.archlinux.org/mirrorlist/?country={{ country_code }}&protocol=http&ip_version=4&use_mirror_status=on 15 | dest=/etc/pacman.d/mirrorlist force=yes 16 | owner=root group=root mode=0644 17 | when: update_mirrorlist or not mirrorlist_old.stat.exists 18 | 19 | - name: enable new mirrorlist servers 20 | command: /usr/bin/sed -i 's/#Server/Server/g' /etc/pacman.d/mirrorlist 21 | when: update_mirrorlist or not mirrorlist_old.stat.exists 22 | 23 | - name: refresh the master package lists 24 | pacman: name=pacman state=installed update_cache=yes 25 | when: update_mirrorlist or update_pkgcache 26 | or not mirrorlist_old.stat.exists 27 | tags: pkgcache 28 | -------------------------------------------------------------------------------- /roles/common/tasks/ntp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook configures NTP (Network Time Protocol). 3 | 4 | - name: install the ntp package 5 | pacman: name=ntp state=installed 6 | 7 | - name: configure the ntp daemon 8 | template: src=ntp.conf.j2 dest=/etc/ntp.conf 9 | owner=root group=root mode=0644 10 | notify: restart the ntp daemon 11 | 12 | - name: start the ntp daemon and enable it on boot 13 | service: name=ntpd state=started enabled=true 14 | -------------------------------------------------------------------------------- /roles/common/tasks/openssh.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook configures OpenSSH. 3 | 4 | - name: configure the openssh daemon 5 | template: src=sshd_config.j2 dest=/etc/ssh/sshd_config 6 | owner=root group=root mode=0644 7 | notify: restart the openssh daemon 8 | 9 | - name: start the openssh daemon and enable it on boot 10 | service: name=sshd state=started enabled=true 11 | -------------------------------------------------------------------------------- /roles/common/tasks/sysctl.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook configures runtime kernel parameters. 3 | 4 | - name: enable reverse path filtering 5 | sysctl: name=net.ipv4.conf.all.rp_filter value={{ net_ipv4_conf_all_rp_filter }} 6 | state=present sysctl_file=/etc/sysctl.d/net.conf 7 | 8 | - name: protect against tcp time-wait assassination hazards 9 | sysctl: name=net.ipv4.tcp_rfc1337 value={{ net_ipv4_tcp_rfc1337 }} 10 | state=present sysctl_file=/etc/sysctl.d/net.conf 11 | 12 | - name: allow reuse of sockets in time-wait state 13 | sysctl: name=net.ipv4.tcp_tw_reuse value={{ net_ipv4_tcp_tw_reuse }} 14 | state=present sysctl_file=/etc/sysctl.d/net.conf 15 | -------------------------------------------------------------------------------- /roles/common/tasks/users.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook handles user creation and access rights. 3 | 4 | - name: create the admin user 5 | user: name="{{ admin_user }}" uid={{ admin_uid }} group=users 6 | tags: bootstrap 7 | 8 | - name: add ssh pubkey to ~/.ssh/authorized_keys for the admin user 9 | authorized_key: user="{{ admin_user }}" key="{{ item }}" 10 | with_file: ssh_pubkey 11 | tags: bootstrap 12 | 13 | - name: grant full sudo rights to the admin user 14 | template: src=sudoers.j2 dest="/etc/sudoers.d/10_{{ admin_user }}" 15 | owner=root group=root mode=0440 16 | validate="visudo -cf %s" 17 | tags: bootstrap 18 | -------------------------------------------------------------------------------- /roles/common/tasks/vnstat.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook configures vnStat for network traffic monitoring. 3 | 4 | - name: install the vnstat package 5 | pacman: name=vnstat state=installed 6 | 7 | - name: start the vnstat daemon and enable it on boot 8 | service: name=vnstat state=started enabled=true 9 | -------------------------------------------------------------------------------- /roles/common/templates/ip6tables.rules.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | *filter 4 | 5 | :INPUT DROP [0:0] 6 | :FORWARD DROP [0:0] 7 | :OUTPUT ACCEPT [0:0] 8 | 9 | # custom chains 10 | :TCP - [0:0] 11 | :UDP - [0:0] 12 | 13 | # prevent ping floods by rate limiting each source address 14 | -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m recent --set --name PING6 --mask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff --rsource 15 | -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -m recent --update --seconds 4 --hitcount 6 --name PING6 --mask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff --rsource -j DROP 16 | -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ACCEPT 17 | 18 | # basic stateful filtering 19 | -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 20 | -A INPUT -i lo -j ACCEPT 21 | -A INPUT -m conntrack --ctstate INVALID -j DROP 22 | -A INPUT -p udp -m conntrack --ctstate NEW -j UDP 23 | -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP 24 | -A INPUT -p udp -j REJECT --reject-with icmp6-port-unreachable 25 | -A INPUT -p tcp -j REJECT --reject-with tcp-reset 26 | -A INPUT -j REJECT --reject-with icmp6-port-unreachable 27 | 28 | # add accept rules to the TCP and UDP chains accordingly 29 | 30 | COMMIT 31 | -------------------------------------------------------------------------------- /roles/common/templates/iptables.rules.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | *filter 4 | 5 | :INPUT DROP [0:0] 6 | :FORWARD DROP [0:0] 7 | :OUTPUT ACCEPT [0:0] 8 | 9 | # custom chains 10 | :TCP - [0:0] 11 | :UDP - [0:0] 12 | 13 | # prevent ping floods by rate limiting each source address 14 | -A INPUT -p icmp -m icmp --icmp-type 8 -m recent --set --name PING --mask 255.255.255.255 --rsource 15 | -A INPUT -p icmp -m icmp --icmp-type 8 -m recent --update --seconds 4 --hitcount 6 --name PING --mask 255.255.255.255 --rsource -j DROP 16 | -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT 17 | 18 | # basic stateful filtering 19 | -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 20 | -A INPUT -i lo -j ACCEPT 21 | -A INPUT -p ipv6 -j ACCEPT 22 | -A INPUT -m conntrack --ctstate INVALID -j DROP 23 | -A INPUT -p udp -m conntrack --ctstate NEW -j UDP 24 | -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP 25 | -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable 26 | -A INPUT -p tcp -j REJECT --reject-with tcp-reset 27 | -A INPUT -j REJECT --reject-with icmp-proto-unreachable 28 | 29 | # add accept rules to the TCP and UDP chains accordingly 30 | -A TCP -p tcp -m tcp --dport 22 -m comment --comment "ssh" -j ACCEPT 31 | {% if cjdns_port.rc is defined and cjdns_port.rc == 0 %} 32 | -A UDP -p udp -m udp --dport {{ cjdns_port.stdout }} -m comment --comment "cjdns" -j ACCEPT 33 | {% endif %} 34 | 35 | COMMIT 36 | -------------------------------------------------------------------------------- /roles/common/templates/ntp.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | # With the default settings below, ntpd will only synchronize your clock. 4 | # 5 | # For details, see: 6 | # - the ntp.conf man page 7 | # - http://support.ntp.org/bin/view/Support/GettingStarted 8 | # - https://wiki.archlinux.org/index.php/Network_Time_Protocol_daemon 9 | 10 | # Associate to public NTP pool servers; see http://www.pool.ntp.org/ 11 | {% for server in ntp_servers %} 12 | server {{ server }} iburst 13 | {% endfor %} 14 | 15 | # Only allow read-only access from localhost 16 | restrict default noquery nopeer 17 | restrict 127.0.0.1 18 | restrict ::1 19 | 20 | # Location of drift file 21 | driftfile /var/lib/ntp/ntp.drift 22 | 23 | # NOTE: If you run dhcpcd and have lines like 'restrict' and 'fudge' appearing 24 | # here, be sure to add '-Y -N' to the dhcpcd_ethX variables in /etc/conf.d/net 25 | -------------------------------------------------------------------------------- /roles/common/templates/sshd_config.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | # This is the sshd server system-wide configuration file. 4 | # See sshd_config(5) for more information. 5 | 6 | {% for address in sshd_listenaddress %} 7 | ListenAddress {{ address }} 8 | {% endfor %} 9 | LoginGraceTime 20 10 | PermitRootLogin no 11 | MaxAuthTries 4 12 | MaxSessions 5 13 | AuthorizedKeysFile .ssh/authorized_keys 14 | PasswordAuthentication no 15 | ChallengeResponseAuthentication no 16 | UsePAM yes 17 | PrintMotd no 18 | UsePrivilegeSeparation sandbox 19 | UseDNS no 20 | MaxStartups 3:50:10 21 | Subsystem sftp /usr/lib/ssh/sftp-server 22 | -------------------------------------------------------------------------------- /roles/common/templates/sudoers.j2: -------------------------------------------------------------------------------- 1 | {{ admin_user }} ALL=(ALL) NOPASSWD: ALL 2 | -------------------------------------------------------------------------------- /site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This playbook deploys the whole application stack in this site. 3 | 4 | - name: apply the common server configuration 5 | hosts: all 6 | sudo: yes 7 | tags: common 8 | roles: 9 | - common 10 | 11 | - name: configure cjdns-related software 12 | hosts: all 13 | sudo: yes 14 | tags: cjdns 15 | roles: 16 | - cjdns 17 | --------------------------------------------------------------------------------