Blog

Installing PeerVPN with Ansible

Installing PeerVPN with Ansible

In addition to the article about the PeerVPN installation and configuration, I will now show you more advanced and quite ‘modern’ way to provision several servers and get your VPN client up really fast. You’ve probably heard of Ansible already. Well, one of its use cases is exactly what we need here: Configuration Manager. Many of us have experienced The Headache, when you need to install, configure and then administer a whole environment. Yes, to repeat the same steps on hundreds of servers, where you have different OS distributions, application versions and all kind of dependencies, and all of that certainly lead to some problems.

Well, Ansible is here to help you with all that stuff. You can choose, set and customize anything that is required for specific environment and suit its needs. So, let us start with the introduction to ansible, its structure and components.
In my opinion there are two approaches when you first start with Ansible. The first one is to read the official introduction to Ansible, which explains a lot about its structure and then start with simple playbook which you then extend to a role. Or the second one, where you make use of the Ansible Galaxy, which has a lot of community-provided roles open for use. Well not every role is that much scalable and flexible as you want so you can simply combine both approaches, take an already built role and expand its functionalities. If you learn that quick and all of that is boring, you can start building your own Ansible modules.

Now, lets have a look at my PeerVPN role.


The directory structure:
  roles/
    PeerVPN/                       # this hierarchy represents a "role"
        tasks/                         #
            main.yml                 # <-- the main. yml file task file includes install.yml + configure.yml
            install.yml                #
            configure.yml          #
        templates/                   # <-- files for use with the template resource
            peervpn.service.j2  # <---templates end in .j2, moved in a certain location
            peervpn.conf.j2       #
        defaults/                      #
            main.yml                 # <-- default lower priority variables for this



Tasks/main.yml:
- include: install.yml
- include: configure.yml
- service: name=peervpn state=started
Tasks/install.yml (NOTE the blankspaces at the start of the line)::
- name: install dependencies
  yum:
    name: "{{ item }}"
    state: latest
  with_items:
    - gcc
    - openssl-devel
    - openssl 
    - python-urllib3
    - pyOpenSSL
  when: ansible_os_family == "RedHat"
- name: downloading PeerVPN
  unarchive: src=https://www.peervpn.net/files/peervpn-0-044-linux-x86.tar.gz dest=/tmp/ remote_src=yes
- name: moving extracted files [binary + config]
  command: /usr/bin/mv /tmp/peervpn-0-044/peervpn /usr/sbin/peervpn
  when: ansible_os_family == "RedHat"

Tasks/configure.yml (NOTE the blankspaces at the start of the line):
- name: create configuration directory
  file:
    path: /etc/peervpn
    state: directory
- name: write the peervpn config file
  template: src=../templates/peervpn.conf.j2 dest=/etc/peervpn/peervpn.conf
- name: create peervpn service
  template: src=../templates/peervpn.service.j2 dest=/usr/lib/systemd/system/peervpn.service
  when: ansible_os_family == "RedHat"
Templates/peervpn.service.j2:
[Unit]
Description={{ unit_description }}
{% if vpn_initpeers -%}
Requires={{ unit_requires }}
{% endif -%}
After={{ unit_after }}
ConditionPathExists={{ unit_condition_path }}
[Service]
Type={{ service_type }}
ExecStart={{ service_exec }}
PIDFile={{ service_pidfile }}
[Install]
WantedBy={{ install_wantedby }}
Templates/peervpn.conf.j2:
port {{ peervpn_port }}
networkname {{ peervpn_networkname }}
psk {{ peervpn_password }}
enabletunneling {{ peevpn_enabletunneling }}
interface {{ peervpn_interface }}
ifconfig4 {{ peervpn_ip }}
{% if vpn_initpeers -%}
initpeers {{ vpn_initpeers }}
{% endif -%} 
Defaults/main.yml:
peervpn_port: 7000
peervpn_networkname: MyPeerVPN
peervpn_password: VqmWsJje18/qqIOEN2+pLHugDtAcN5So03J0TL9wIQomAkQwcNR23SzM+m7cuJyCeyXXuHDLoKbzVbk176eDVLPS8wLNoWSiwPF2yQn57Q4vjRz3qjI51nhE3yEyKmHZgotQga4Uz0vyvTGjJRxtPJqZ/igDN0YmI25nwhsYsyi34c6pIcqPCOHYlILndCUh8AYk3hGNPc0lSnkxW/sY+Uo+5BU0K6nB1LYcMlXA9Ij0deU+eyRTfoVggKnpXdl5FikrELbAOyoo71F0PEjL73k5fmyGmsoEE1f4yvgUXOKOzsPUaf567SP4Hm+h/EpqkWMR7JGc5jBDsOlY52LTp5XaZNn+l+VEInCsFQiKgTW3zvzSmUcrPB3GqQ1KXknwfHmtCJe4SRiRZLKtSeTiPdPvXC0HBMk+KK9TNk45dDUi3/ougCIwavMPEedH7Gh5fvG5iNXjZ24tAAOTle2oBHVgO2Wq7llh2VwKkrzyYATuchusVLbHa98D0YR2wA==
peevpn_enabletunneling: "yes"
peervpn_interface: pvpn0
peervpn_ip: 0.0.0.0
vpn_initpeers: false
unit_description: PeerVPN
unit_requires: network.target
unit_after: syslog.target network.target
unit_condition_path: /etc/peervpn/peervpn.conf
service_type: simple
service_exec: /usr/sbin/peervpn {{ unit_condition_path }}
service_pidfile: /var/run/peervpn/peervpn.pid
install_wantedby: multi-user.target


So, now we have the role. The only thing left is the playbook and the inventory file. In fact the inventory file can be skipped(you can parse the hosts as arguments when you run the command) or your target hosts can be placed in the default Ansible inventory file in /etc/ansible/hosts. Here is an example playbook, which is provisioning 2 hosts:

playbook.yml:
- name: Configuring and installing PeerVPN init peer
  hosts: VPN_init_peer 
  remote_user: root
  vars:
    peervpn_ip: 10.1.0.1/24
  roles:
    - PeerVPN
- name: Configuring and installing PeerVPN other peers
  hosts: Host_1
  remote_user: root
  vars:
    peervpn_ip: 10.1.0.2/24
    vpn_initpeers: "{{ groups.VPN_init_peer[0] }} {{ peervpn_port }}"
  roles:
    - PeerVPN

 Note that I am using an inventory file for keeping the hosts:

Inventory.yml:
[VPN_init_peer]
165.165.165.165
[Host_1]
170.170.170.170

To provision more hosts you need to simply add [Host_<2/3/4..etc>] and an IP in your inventory file and add acordingly paragraph(s) to the playbook file, with different peervpn_ip and hosts.


To run the above example, you need to have your public keys distributed on the target hosts because Ansible works over SSH by default. Now simply execute the following command in the dir where your playbook is: 
$ ansible-playbook playbook.yml -i inventory.yml