├── .gitignore ├── LICENSE ├── README.md ├── ansible.cfg ├── apps ├── elixir-build-server │ ├── circle_ci.pub │ ├── host_vars │ │ └── elixir-build-server.lunarlogic.io │ ├── inventory │ └── playbook.yml └── phoenix-website │ ├── circle_ci.pub │ ├── host_vars │ └── phoenix-website.lunarlogic.io │ ├── inventory │ └── playbook.yml ├── callback_plugins └── human_log.py ├── play ├── public_keys ├── artur.trzop.pub └── marek.ciupak.pub ├── requirements.yml └── roles ├── bootstrap └── 0.0.1 │ └── tasks │ ├── hostname.yml │ ├── locale.yml │ ├── main.yml │ ├── set_authorized_keys.yml │ ├── upgrade.yml │ └── utils.yml ├── configure-mysql └── 0.0.1 │ ├── tasks │ └── main.yml │ └── templates │ └── my.cnf.j2 ├── configure-postgresql └── 0.0.1 │ └── tasks │ └── main.yml ├── imagemagick └── 0.0.1 │ └── tasks │ └── main.yml ├── install-build-essential └── 0.0.1 │ └── tasks │ └── main.yml ├── install_elixir └── 0.0.1 │ └── tasks │ └── main.yml ├── install_mysql └── 0.0.1 │ └── tasks │ └── main.yml ├── install_nginx └── 0.0.1 │ └── tasks │ └── main.yml ├── install_nodejs └── 0.0.1 │ ├── defaults │ └── main.yml │ └── tasks │ └── main.yml ├── install_postgresql └── 0.0.1 │ └── tasks │ └── main.yml ├── install_yarn └── 0.0.1 │ └── tasks │ └── main.yml ├── lets_encrypt └── 0.0.1 │ ├── defaults │ └── main.yml │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ ├── cli.ini.j2 │ ├── ssl-params.conf.j2 │ └── well-known.conf.j2 ├── phoenix-app └── 0.0.1 │ ├── README.md │ ├── handlers │ └── main.yml │ ├── tasks │ ├── app_server.yml │ ├── configure_nginx.yml │ └── main.yml │ └── templates │ ├── app_name.service.j2 │ └── nginx.j2 ├── provision_log └── 0.0.1 │ ├── defaults │ └── main.yml │ └── tasks │ └── main.yml └── user └── 0.0.1 ├── defaults └── main.yml └── tasks └── main.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vagrant 3 | ubuntu-xenial-16.04-cloudimg-console.log 4 | *.retry 5 | callback_plugins/human_log.pyc 6 | /.downloaded_roles 7 | 8 | # ignore private pass that you use to encrypt/decrypt credentials with Ansible 9 | vault_pass.txt 10 | 11 | # ignore private keys for CI server 12 | /apps/elixir-build-server/circle_ci 13 | /apps/phoenix-website/circle_ci 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Lunar Logic https://lunarlogic.io 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Elixir Playbooks 2 | 3 | This project has ansible playbooks for: 4 | 5 | * Elixr Build Server - it has installed Erlang, Elixir and nodejs. Basically, all what is required to compile the Phoenix Framework application. 6 | * Phoenix Website - it is a playbook to provision server with installed PostgreSQL and configured nginx and Let's Encrypt for SSL. There is no Erlang/Elixir on this server because we will deploy there only compiled Phoenix application. 7 | 8 | __You can learn more about the project from this blog post__ 9 | https://blog.lunarlogic.io/2017/phoenix-app-deployment-with-ansible-playbooks-for-elixir/ 10 | 11 | Here you will find an example [Phoenix Framework app configured for deployment](https://github.com/LunarLogic/phoenix_website). 12 | 13 | ## Requirements 14 | 15 | ### Control machine (your computer) 16 | 17 | * Install [Ansible](https://www.ansible.com/) 18 | 19 | * Download roles: 20 | 21 | ```shell 22 | $ ansible-galaxy install -r requirements.yml 23 | ``` 24 | 25 | * Generate `vault_pass.txt` file into this repository. You need it to be able to encrypt/decrypt secrets. 26 | 27 | :warning: For security reasons, the `vault_pass.txt` file should not be committed into the repository. It's ignored in `.gitignore`. 28 | 29 | ```shell 30 | $ openssl rand -base64 256 > vault_pass.txt 31 | ``` 32 | 33 | * Generate your DB password and put output to `apps/phoenix-website/host_vars/phoenix-website.lunarlogic.io` 34 | 35 | ```shell 36 | $ ansible-vault encrypt_string --name db_password "YOUR_DB_PASSWORD" 37 | ``` 38 | 39 | ### Target machine (server) 40 | 41 | * Server with [Ubuntu](https://www.ubuntu.com/) 16.04 LTS. 42 | 43 | ## Public keys 44 | 45 | We keep our public keys in [public_keys/] directory. This set of keys is uploaded to the server during each provisioning 46 | and overwrites the list of authorized keys, so proper people have access to the server. It is important to keep 47 | the list of keys up to date. 48 | 49 | If you don't know how to generate a key for yourself, 50 | [read this article](https://help.github.com/articles/connecting-to-github-with-ssh/). 51 | 52 | ## App deployement 53 | 54 | ### CircleCI deployment 55 | 56 | If you want to deploy app from CI to the staging/production host then you must generate RSA keys for CircleCI. 57 | 58 | ```shell 59 | $ ssh-keygen -t rsa -b 4096 -N "" -C "circle_ci" -f ./apps/elixir-build-server/circle_ci 60 | $ ssh-keygen -t rsa -b 4096 -N "" -C "circle_ci" -f ./apps/phoenix-website/circle_ci 61 | ``` 62 | 63 | Add `circle_ci.pub` public key to your app playbook for the role `user`: 64 | 65 | ``` 66 | - role: user/0.0.1 67 | username: phoenix 68 | authorized_key_paths: 69 | - ../../public_keys/*.pub 70 | - ./circle_ci.pub # add this line 71 | ``` 72 | 73 | Go to CircleCI and find your project, open settings and find `SSH Permissions`. Click `Add an SSH key` button and paste there private key `apps/YOUR_APP_NAME/circle_ci`. 74 | 75 | Now you can remove private key `apps/YOUR_APP_NAME/circle_ci` from local machine. It should not be commited into repo! 76 | 77 | Commit into repo only public key `apps/YOUR_APP_NAME/circle_ci.pub`. 78 | 79 | You can always generate a new fresh keys if you need it hence no reason to backup private key. You already added it to CircleCI. 80 | 81 | ## Run playbooks 82 | 83 | __Warning:__ This command will provision all servers listed in inventory file for particular app `apps/app_name`. 84 | 85 | ```shell 86 | $ ./play apps/app_name 87 | ``` 88 | 89 | If you want to provision only specific machine do (it's useful if your app is deployed to multiple servers like staging and production): 90 | 91 | ```shell 92 | # Warning: There must be comma and the end of the hosts list! 93 | $ ansible-playbook -i 'example-staging.lunarlogic.io,' apps/app_name/playbook.yml 94 | ``` 95 | 96 | ## Provisioning logs 97 | 98 | You can check when and with what git commit the host was provisioned in log file: `/var/log/provision.log` (stored on the target machine). 99 | 100 | ## System users 101 | 102 | There are 3 types of users on the server: 103 | 104 | * `root` - for provisioning 105 | * `admin` - user has the sudo access 106 | * `app_name_user` - for instance `phoenix` user for Phoenix Website application. The user has no sudo access. The application is running under this user. 107 | 108 | ## Add playbook for new app 109 | 110 | * create new app directory in the `app` directory 111 | * in this new directory create `playbook.yml` and `inventory` files 112 | * in the `inventory` file put host names to provision (see [Ansible docs](http://docs.ansible.com/ansible/intro_inventory.html)) 113 | * implement `playbook.yml` 114 | 115 | ## Secrets 116 | 117 | We store secrets in encrypted version using [Vault]. If you are adding new secrets, make sure you commit them to the repository in the encrypted form. 118 | 119 | * Encrypting single values (that can be placed inside a "clear text" YAML file, using the `!vault` tag): 120 | 121 | ```shell 122 | $ ansible-vault encrypt_string --name pass_to_some_service "secret" # stdout encrypted string 123 | ``` 124 | 125 | * Encrypting whole YAML files: 126 | 127 | ```shell 128 | $ ansible-vault encrypt secret.yml # encrypt unencrypted file 129 | $ ansible-vault edit secret.yml # edit encrypted file 130 | $ ansible-vault decrypt secret.yml # decrypt encrypted file 131 | ``` 132 | 133 | ## Roles 134 | 135 | ### Role versioning 136 | 137 | We use roles versioning the simplest possible way, we just add version subdirectories under every role directory. 138 | 139 | ```shell 140 | roles/role-name/role-version/ # e.g. roles/webserver/0.3.2/ 141 | ``` 142 | 143 | To create a new version just copy an existing one, bump the role version and modify it. 144 | Please, respect [Semantic Versioning 2.0.0]. 145 | 146 | ### Community developed roles 147 | 148 | Include the roles in [requirements.yml] and download them using the following command: 149 | 150 | ```shell 151 | $ ansible-galaxy install -r requirements.yml 152 | ``` 153 | 154 | ### SSL with Let's Encrypt 155 | 156 | You can use `lets_encrypt` role to generate free SSL certificate thanks to https://letsencrypt.org 157 | 158 | #### Rate Limits 159 | 160 | The main limit is Certificates per Registered Domain (20 per week). 161 | 162 | https://letsencrypt.org/docs/rate-limits/ 163 | 164 | If you are testing Let's Encrypt then use `staging` environment with higher limits! 165 | 166 | ``` 167 | - role: lets_encrypt/0.0.1 168 | app_name: myapp 169 | lets_encrypt_contact_email: admin@lunarlogic.io 170 | lets_encrypt_environment: staging # you can change it to production once ready 171 | ``` 172 | 173 | #### If you want to change main domain for your certificate 174 | 175 | If you want to change main domain for your certificate then you need to generate a new certificate. 176 | 177 | Here is example file for [Phoenix Website project with multiple domains](apps/phoenix-website/host_vars/phoenix-website.lunarlogic.io). 178 | 179 | In order to generate a new certificate please remove first the old files generated by `lets_encrypt` role on the server: 180 | 181 | ```shell 182 | $ rm -rf /etc/letsencrypt/accounts/* 183 | $ rm -rf /etc/letsencrypt/archive/* 184 | $ rm -rf /etc/letsencrypt/csr/* 185 | $ rm -rf /etc/letsencrypt/keys/* 186 | $ rm -rf /etc/letsencrypt/live/* 187 | $ rm -rf /etc/letsencrypt/renewal/* 188 | 189 | # remove the snippents that load SSL certificate 190 | $ rm -rf /etc/nginx/snippets/project_name 191 | ``` 192 | 193 | Ensure the nginx is running. It's required so Let's Encrypt can do request to our domain. 194 | Provision server again. 195 | 196 | Note: If you would like to add a new subdomain to domain list then you can just provision server and a new subdomain will be added to the certificate. You need to generate certificate from scrach only if you change the main domain (the first domain on the list of domains). 197 | 198 | 199 | [Vault]: http://docs.ansible.com/ansible/playbooks_vault.html 200 | [public_keys/]: public_keys/ 201 | [requirements.yml]: requirements.yml 202 | [Semantic Versioning 2.0.0]: http://semver.org/ 203 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | vault_password_file = ./vault_pass.txt 3 | roles_path = ./.downloaded_roles:./roles 4 | callback_plugins = ./callback_plugins 5 | 6 | [ssh_connection] 7 | control_path = %(directory)s/%%C 8 | -------------------------------------------------------------------------------- /apps/elixir-build-server/circle_ci.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7MmV55/7vG7kCYmwwVVOYRGz2bT2RHlSmoAVGUZ95+hDdVIRKZsP3zs+jm0Kr08RlYlw1BToH0yyZK3rjq7QARfmw1zmwtVtccP3nscHT1KQyx+docjYdmsTLGm7ylKyHrQ+lOq80/rCMsnxf0S+/fxGEThGgh0zxRsEZUz0AofSqEgrm10PBcPdw7m6RE8DTEArjXaQRwhrlcMyEHaEvAVpLhEKjpE/9DY8ZJB4y8bxXlr0ORBaw0G6IPXVSOUaWNiJqgJ+OyzSqiMTqKDqg6BhSetcDCEMn3VW+kQquqbuU8qp6Yr8XjG+NXDb6gt4S48TdqQg/o4uZnNPUDW8otyyAcHBpVqdF1nNwqE80wvDHHpOytfD/+s9M2l43s6f/RV71myghtALxFKMo44Fzw30L0gsL79iQw8WXClI1bdBdjLiSG3vj67LKk+OCy335816VsfTYU7Oh7H6TUc5jooiUPKAopLF9Qm9WSFm8B1CW1UdajrWzT6SAdScFxaQbG6IUX4EDz32jz3mpDFN8huLCXkmgDctynhTROHJxq3vbOSOi8gGYUIDJuSbe2OvuMSZ1wILPqu5k1Tw5zMUC63Ggqi9TfRt++eX30hU4YVIyNJJEaiMmGTzSnN9/awUl5bqdmPkwQ9xrBhi8U4DR7edtX2LVUHfujpdL6PJNbQ== circle_ci 2 | -------------------------------------------------------------------------------- /apps/elixir-build-server/host_vars/elixir-build-server.lunarlogic.io: -------------------------------------------------------------------------------- 1 | --- 2 | hostname: elixir-build-server 3 | domains: 4 | # first domain is the main domain 5 | - elixir-build-server.lunarlogic.io 6 | -------------------------------------------------------------------------------- /apps/elixir-build-server/inventory: -------------------------------------------------------------------------------- 1 | elixir-build-server.lunarlogic.io 2 | -------------------------------------------------------------------------------- /apps/elixir-build-server/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | user: root 4 | gather_facts: no 5 | pre_tasks: 6 | - name: install python 7 | raw: sudo apt-get -y install python-minimal 8 | 9 | - action: setup 10 | 11 | roles: 12 | - role: bootstrap/0.0.1 13 | authorized_key_paths: 14 | - ../../public_keys/*.pub 15 | 16 | - role: user/0.0.1 17 | username: admin 18 | sudoer: True 19 | authorized_key_paths: 20 | - ../../public_keys/*.pub 21 | 22 | - role: install-build-essential/0.0.1 23 | 24 | - role: install_elixir/0.0.1 25 | 26 | - role: install_nodejs/0.0.1 27 | 28 | - role: user/0.0.1 29 | username: builder 30 | authorized_key_paths: 31 | - ../../public_keys/*.pub 32 | - ./circle_ci.pub 33 | 34 | # This must be the last one role to log info about successfully provisioned machine. 35 | - role: provision_log/0.0.1 36 | -------------------------------------------------------------------------------- /apps/phoenix-website/circle_ci.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQChdYQSoh1rlAS1YBzoB4hMPA/Mm88WqV9ANa/gcxDH4Q+urc5MM35srlv7G9KMY5I0XHeFb1eT0GyldoNnvJFr6ZvtyaUAKloN8s4uavST7xofFhKH0WU3PhVM83Uu6kVDikfwmgTkvaW+dfkW02TWBJWP6taKPGRcVvGk5QISCxYKreUPFj0BIL+d66S2+vp2ypGpEAt3RjHfpa+ExgNnhqNk6pARhwQxGKa2wwgTcfPVdisBJ3cr0N1YTzLz+ko2KYQec2IR/W4gjsubKb7NeJpDkRDmYIqJUomQf5IA8OgXNZ1i/jt6W/3IXdq5wjnZKUAd88PvnFTVHGseY+QEvMlhqsUxZYAFqvbADyofNwilJsy4ZBU4tizR6dHJ4P5G9qT7D1MBx7AwbMiw2z/b2brLgrDcSHD/N/lT3+VJAASNQrn42PB+WaaDQhkCSPCVM+3njQisO24ARQNsqfRcaqDv/faBJKqLcRsk3QQkGgqaJVXWHLsLYAQxALsOz8QA2bWrO3rVy6vjFtLzgosgmB7zHfb39Pv966AWBn5BicYMCw1Pa2YEqNxcXcNOvXnquSL0IxCVTedT+hiJg8WKcRNbeZ0ZsW1zVIQW42X70nKlnC5S3aAx+6II8aVdXaSgjTypidkrv/n6mRimdNW+2tQCEHPMU5TQaDBXUHL20w== circle_ci 2 | -------------------------------------------------------------------------------- /apps/phoenix-website/host_vars/phoenix-website.lunarlogic.io: -------------------------------------------------------------------------------- 1 | --- 2 | hostname: phoenix 3 | domains: 4 | # first domain is the main domain 5 | - phoenix-website.lunarlogic.io 6 | # other domains redirect to the main domain 7 | - www.phoenix-website.lunarlogic.io 8 | db_password: !vault | 9 | $ANSIBLE_VAULT;1.1;AES256 10 | 35313965306661613265333736386365613931643764616533613137626161623232393261653832 11 | 6631393234313761303539316335373539663965613963620a663831366338383934346632643065 12 | 33646165303366623464623730363530656438373262373937396164383963396262393266343035 13 | 6137663666353562640a383361653635396437313936373365313431303734316461326364336538 14 | 63356331666263643665306561386664636138646265663036646331383037643531393366613963 15 | 3536313631373166303865303636393038396365306466376166 16 | -------------------------------------------------------------------------------- /apps/phoenix-website/inventory: -------------------------------------------------------------------------------- 1 | phoenix-website.lunarlogic.io 2 | -------------------------------------------------------------------------------- /apps/phoenix-website/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | user: root 4 | gather_facts: no 5 | pre_tasks: 6 | - name: install python 7 | raw: sudo apt-get -y install python-minimal 8 | 9 | - action: setup 10 | 11 | roles: 12 | - role: bootstrap/0.0.1 13 | authorized_key_paths: 14 | - ../../public_keys/*.pub 15 | 16 | - role: user/0.0.1 17 | username: admin 18 | sudoer: True 19 | authorized_key_paths: 20 | - ../../public_keys/*.pub 21 | 22 | - role: install_nginx/0.0.1 23 | 24 | - role: install_postgresql/0.0.1 25 | 26 | - role: imagemagick/0.0.1 27 | 28 | - role: user/0.0.1 29 | username: phoenix 30 | authorized_key_paths: 31 | - ../../public_keys/*.pub 32 | - ./circle_ci.pub 33 | 34 | - role: configure-postgresql/0.0.1 35 | username: phoenix 36 | database_name: phoenix 37 | 38 | - role: phoenix-app/0.0.1 39 | username: phoenix 40 | app_name: phoenix_website 41 | 42 | - role: lets_encrypt/0.0.1 43 | app_name: phoenix_website 44 | lets_encrypt_contact_email: admin@lunarlogic.io 45 | lets_encrypt_environment: production 46 | 47 | # This must be the last one role to log info about successfully provisioned machine. 48 | - role: provision_log/0.0.1 49 | -------------------------------------------------------------------------------- /callback_plugins/human_log.py: -------------------------------------------------------------------------------- 1 | # This program is free software: you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation, either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | 14 | # Inspired from: https://github.com/redhat-openstack/khaleesi/blob/master/plugins/callbacks/human_log.py 15 | # Further improved support Ansible 2.0 16 | 17 | from __future__ import (absolute_import, division, print_function) 18 | __metaclass__ = type 19 | 20 | import sys 21 | reload(sys) 22 | sys.setdefaultencoding('utf-8') 23 | 24 | try: 25 | import simplejson as json 26 | except ImportError: 27 | import json 28 | 29 | # Fields to reformat output for 30 | FIELDS = ['cmd', 'command', 'start', 'end', 'delta', 'msg', 'stdout', 31 | 'stderr', 'results'] 32 | 33 | 34 | class CallbackModule(object): 35 | 36 | """ 37 | Ansible callback plugin for human-readable result logging 38 | """ 39 | CALLBACK_VERSION = 2.0 40 | CALLBACK_TYPE = 'notification' 41 | CALLBACK_NAME = 'human_log' 42 | CALLBACK_NEEDS_WHITELIST = False 43 | 44 | def human_log(self, data): 45 | if type(data) == dict: 46 | for field in FIELDS: 47 | no_log = data.get('_ansible_no_log') 48 | if field in data.keys() and data[field] and no_log != True: 49 | output = self._format_output(data[field]) 50 | print("\n{0}: {1}".format(field, output.replace("\\n","\n"))) 51 | 52 | def _format_output(self, output): 53 | # Strip unicode 54 | if type(output) == unicode: 55 | output = output.encode(sys.getdefaultencoding(), 'replace') 56 | 57 | # If output is a dict 58 | if type(output) == dict: 59 | return json.dumps(output, indent=2) 60 | 61 | # If output is a list of dicts 62 | if type(output) == list and type(output[0]) == dict: 63 | # This gets a little complicated because it potentially means 64 | # nested results, usually because of with_items. 65 | real_output = list() 66 | for index, item in enumerate(output): 67 | copy = item 68 | if type(item) == dict: 69 | for field in FIELDS: 70 | if field in item.keys(): 71 | copy[field] = self._format_output(item[field]) 72 | real_output.append(copy) 73 | return json.dumps(output, indent=2) 74 | 75 | # If output is a list of strings 76 | if type(output) == list and type(output[0]) != dict: 77 | # Strip newline characters 78 | real_output = list() 79 | for item in output: 80 | if "\n" in item: 81 | for string in item.split("\n"): 82 | real_output.append(string) 83 | else: 84 | real_output.append(item) 85 | 86 | # Reformat lists with line breaks only if the total length is 87 | # >75 chars 88 | if len("".join(real_output)) > 75: 89 | return "\n" + "\n".join(real_output) 90 | else: 91 | return " ".join(real_output) 92 | 93 | # Otherwise it's a string, (or an int, float, etc.) just return it 94 | return str(output) 95 | 96 | def on_any(self, *args, **kwargs): 97 | pass 98 | 99 | def runner_on_failed(self, host, res, ignore_errors=False): 100 | self.human_log(res) 101 | 102 | def runner_on_ok(self, host, res): 103 | self.human_log(res) 104 | 105 | def runner_on_skipped(self, host, item=None): 106 | pass 107 | 108 | def runner_on_unreachable(self, host, res): 109 | self.human_log(res) 110 | 111 | def runner_on_no_hosts(self): 112 | pass 113 | 114 | def runner_on_async_poll(self, host, res, jid, clock): 115 | self.human_log(res) 116 | 117 | def runner_on_async_ok(self, host, res, jid): 118 | self.human_log(res) 119 | 120 | def runner_on_async_failed(self, host, res, jid): 121 | self.human_log(res) 122 | 123 | def playbook_on_start(self): 124 | pass 125 | 126 | def playbook_on_notify(self, host, handler): 127 | pass 128 | 129 | def playbook_on_no_hosts_matched(self): 130 | pass 131 | 132 | def playbook_on_no_hosts_remaining(self): 133 | pass 134 | 135 | def playbook_on_task_start(self, name, is_conditional): 136 | pass 137 | 138 | def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None): 139 | pass 140 | 141 | def playbook_on_setup(self): 142 | pass 143 | 144 | def playbook_on_import_for_host(self, host, imported_file): 145 | pass 146 | 147 | def playbook_on_not_import_for_host(self, host, missing_file): 148 | pass 149 | 150 | def playbook_on_play_start(self, name): 151 | pass 152 | 153 | def playbook_on_stats(self, stats): 154 | pass 155 | 156 | def on_file_diff(self, host, diff): 157 | pass 158 | 159 | 160 | ####### V2 METHODS ###### 161 | def v2_on_any(self, *args, **kwargs): 162 | pass 163 | 164 | def v2_runner_on_failed(self, result, ignore_errors=False): 165 | self.human_log(result._result) 166 | 167 | def v2_runner_on_ok(self, result): 168 | self.human_log(result._result) 169 | 170 | def v2_runner_on_skipped(self, result): 171 | pass 172 | 173 | def v2_runner_on_unreachable(self, result): 174 | self.human_log(result._result) 175 | 176 | def v2_runner_on_no_hosts(self, task): 177 | pass 178 | 179 | def v2_runner_on_async_poll(self, result): 180 | self.human_log(result._result) 181 | 182 | def v2_runner_on_async_ok(self, host, result): 183 | self.human_log(result._result) 184 | 185 | def v2_runner_on_async_failed(self, result): 186 | self.human_log(result._result) 187 | 188 | def v2_playbook_on_start(self, playbook): 189 | pass 190 | 191 | def v2_playbook_on_notify(self, result, handler): 192 | pass 193 | 194 | def v2_playbook_on_no_hosts_matched(self): 195 | pass 196 | 197 | def v2_playbook_on_no_hosts_remaining(self): 198 | pass 199 | 200 | def v2_playbook_on_task_start(self, task, is_conditional): 201 | pass 202 | 203 | def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None, 204 | encrypt=None, confirm=False, salt_size=None, 205 | salt=None, default=None): 206 | pass 207 | 208 | def v2_playbook_on_setup(self): 209 | pass 210 | 211 | def v2_playbook_on_import_for_host(self, result, imported_file): 212 | pass 213 | 214 | def v2_playbook_on_not_import_for_host(self, result, missing_file): 215 | pass 216 | 217 | def v2_playbook_on_play_start(self, play): 218 | pass 219 | 220 | def v2_playbook_on_stats(self, stats): 221 | pass 222 | 223 | def v2_on_file_diff(self, result): 224 | pass 225 | 226 | def v2_playbook_on_item_ok(self, result): 227 | pass 228 | 229 | def v2_playbook_on_item_failed(self, result): 230 | pass 231 | 232 | def v2_playbook_on_item_skipped(self, result): 233 | pass 234 | 235 | def v2_playbook_on_include(self, included_file): 236 | pass 237 | 238 | def v2_playbook_item_on_ok(self, result): 239 | pass 240 | 241 | def v2_playbook_item_on_failed(self, result): 242 | pass 243 | 244 | def v2_playbook_item_on_skipped(self, result): 245 | pass 246 | -------------------------------------------------------------------------------- /play: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ansible-playbook -i $1/inventory $1/playbook.yml 4 | -------------------------------------------------------------------------------- /public_keys/artur.trzop.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZxq58qGr85KcDlkE0f+OCcAdlRNsRe0pFYE2RO9PTzP0C4VZwJM7IhrV30wRMNfpqPMD8uJ7/NbP9WFDab3wh9Sav6cR+jbHl5IKZrpPKAaKkgHxn0SA6u8ekFU8C6lzOwO8uqqZpX0RQnachLRQi+TCmB/HrveiiudmJAKNWcFUTujjPM9ZFDsi0RnWTSt4lddICerS0+QZZ5Qi5QxaA/78fNhQwHME2/aQn40A6EcAg1IkvKUuE2fCMPPpXDTg7fyVk8tgfQD++XHDbFcU169yb/VwYcdE7+hG/u9Crx8Rgq2x6KkYSyY8uXK0wDBGHzDY2NNcOLtbQQhC6/maV artur.trzop@llp.pl 2 | -------------------------------------------------------------------------------- /public_keys/marek.ciupak.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQD0eoZZnPr6gkHo8don1gxpxS+AsRfMZppl9GdqU52jqVviKvHCp1IAEZok5QfMX9AzUSAQ/FxEAMuzKSRL1wrPAfPYgDwXQH5x2zxlm0VAN4noesUgZJ4pwqrgHLI4f2QW1FRa5Z+ZxsSO0OH4VjDwB+EgaPSn+P4KMCU391AmWWB051iwvjQl+K0HCdhhv4XNNDIvRt/0AfUksn6aIMLgM6NJU1WIrHSyQmMDuE//eZIoLXxKD6DXa3vZ0J2aajIKOZ7EAO6ROoyOr3hgMgIfJWszHSCSiR8gtYSCeK6JSzlg1filALllz+o4UGkqzXIzt0w7VWmxK6AtyuozUedPHQXyU1T6gzmOZX2JhQJipoA/pLQ10wUNzWHj+Ai8UKDSNETC5B3kpi2bLtJrK6AubOSKSLfscm/4n9DE2vWKftGal/49c8kWhhcTHM+EJdrr+Mj+fO7rjHMVsJH29VixDupeZc+fCno8aPi+zhcZ4x9Xs04bhX578Mg2m7zc78YPcfsjyahIfXnoSQLpGxr6fuBc2knGRHuBC4FC8LoIdUdFeXVqA/z3gxKcua/wjLJyRHcXt95Kk5es+EG56V28SOxqbw22UcWlLQIqGmnHiKS/a5c14PFXOownt//46u2HGQYk+V6nqsEz4qO52+WXf9rqcKcrCU+xq50oWOEVrQ== marek.ciupak@lunarlogic.io 2 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | # http://docs.ansible.com/ansible/galaxy.html#installing-multiple-roles-from-a-file 2 | 3 | - src: zzet.rbenv 4 | version: 3.1.0 5 | name: zzet.rbenv/3.1.0 6 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/hostname.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: set fully qualified domain name 3 | lineinfile: 4 | dest: /etc/hosts 5 | state: present 6 | regexp: "^127.0.1.1" 7 | line: "127.0.1.1 {{ hostname }}.{{ domains[0] }} {{ hostname }}" 8 | 9 | - name: set hostname 10 | hostname: 11 | name: "{{ hostname }}" 12 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/locale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install English language pack 3 | apt: 4 | pkg: 'language-pack-en' 5 | update_cache: yes 6 | cache_valid_time: 3600 7 | 8 | - name: export LANGUAGE 9 | shell: export LANGUAGE=en_US.UTF-8 10 | 11 | - name: export LANG 12 | shell: export LANG=en_US.UTF-8 13 | 14 | - name: apt LC_ALL 15 | shell: export LC_ALL=en_US.UTF-8 16 | 17 | - name: generate locale 18 | shell: locale-gen en_US.UTF-8 19 | become: true 20 | 21 | - name: Update locales 22 | shell: update-locale LANG=en_US.UTF-8 LC_ALL=en_US.utf8 23 | become: true 24 | 25 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: upgrade.yml 3 | - include: locale.yml 4 | - include: set_authorized_keys.yml 5 | - include: utils.yml 6 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/set_authorized_keys.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: set authorized keys 3 | authorized_key: 4 | user: "{{ item }}" 5 | state: present 6 | key: "{{ lookup('pipe', 'cat {{ authorized_key_paths | join(\" \") }}') }}" 7 | exclusive: True 8 | with_items: 9 | - root 10 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/upgrade.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set Postfix option type as internet site 3 | debconf: 4 | name: postfix 5 | question: postfix/main_mailer_type 6 | value: Internet Site 7 | vtype: string 8 | 9 | - name: Set Postfix option hostname 10 | debconf: 11 | name: postifx 12 | question: "postfix/mailname" 13 | value: "{{ domains[0] }}" 14 | vtype: string 15 | 16 | - name: "install aptitude" 17 | apt: 18 | name: aptitude 19 | update_cache: yes 20 | cache_valid_time: 3600 21 | 22 | - name: remove not needed packages 23 | apt: 24 | pkg: "{{ item }}" 25 | state: absent 26 | update_cache: yes 27 | cache_valid_time: 3600 28 | with_items: 29 | - apache2 30 | - apache2-doc 31 | - apache2-mpm-prefork 32 | - apache2-utils 33 | - xinetd 34 | - sasl2-bin 35 | - samba 36 | - bind9 37 | 38 | - name: install missing packages 39 | apt: 40 | pkg: "{{ item }}" 41 | state: present 42 | update_cache: yes 43 | cache_valid_time: 3600 44 | with_items: 45 | - apt-transport-https 46 | 47 | - name: apt upgrade 48 | apt: 49 | upgrade: yes 50 | update_cache: yes 51 | cache_valid_time: 3600 52 | 53 | - name: apt update 54 | apt: 55 | upgrade: dist 56 | update_cache: yes 57 | cache_valid_time: 3600 58 | -------------------------------------------------------------------------------- /roles/bootstrap/0.0.1/tasks/utils.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install useful packages 3 | action: apt pkg={{ item }} update_cache=yes cache_valid_time=3600 4 | with_items: 5 | - git 6 | - mc 7 | - vim 8 | - tmux 9 | 10 | - name: set default editor 11 | alternatives: 12 | name: editor 13 | path: /usr/bin/vim.basic 14 | -------------------------------------------------------------------------------- /roles/configure-mysql/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy the root credentials as .my.cnf file 3 | template: src=my.cnf.j2 dest=~/.my.cnf mode=0600 4 | 5 | - name: create a role in MySQL 6 | mysql_user: 7 | name: "{{ username }}" 8 | host: "{{ item }}" 9 | password: "{{ db_password }}" 10 | priv: "*.*:ALL,GRANT" 11 | state: present 12 | with_items: 13 | - "{{ ansible_hostname }}" 14 | - 127.0.0.1 15 | - ::1 16 | - localhost 17 | become: yes 18 | 19 | - name: create a database in MySQL 20 | mysql_db: 21 | name: "{{ database_name }}" 22 | encoding: utf8 23 | state: present 24 | become: yes 25 | -------------------------------------------------------------------------------- /roles/configure-mysql/0.0.1/templates/my.cnf.j2: -------------------------------------------------------------------------------- 1 | [client] 2 | user=root 3 | password= 4 | -------------------------------------------------------------------------------- /roles/configure-postgresql/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create a role in PostgreSQL 3 | postgresql_user: 4 | name: "{{ username }}" 5 | password: "{{ db_password }}" 6 | role_attr_flags: SUPERUSER 7 | become: yes 8 | become_user: postgres 9 | 10 | - name: create a database in PostgreSQL 11 | postgresql_db: 12 | name: "{{ database_name }}" 13 | owner: "{{ username }}" 14 | encoding: UTF8 15 | template: template0 16 | become: yes 17 | become_user: postgres 18 | -------------------------------------------------------------------------------- /roles/imagemagick/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install imagemagick 3 | apt: 4 | pkg: "{{ item }}" 5 | state: present 6 | update_cache: yes 7 | cache_valid_time: 3600 8 | with_items: 9 | - imagemagick 10 | -------------------------------------------------------------------------------- /roles/install-build-essential/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install build-essential 3 | apt: 4 | pkg: build-essential 5 | state: present 6 | update_cache: yes 7 | cache_valid_time: 3600 8 | -------------------------------------------------------------------------------- /roles/install_elixir/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add an apt ubuntu/erlang_solutions signing key 3 | apt_key: 4 | url: https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc 5 | state: present 6 | 7 | - name: add erlang-solutions repository into sources list 8 | apt_repository: 9 | repo: deb https://packages.erlang-solutions.com/ubuntu xenial contrib 10 | state: present 11 | 12 | - name: install packages 13 | apt: 14 | pkg: "{{ item }}" 15 | state: present 16 | update_cache: yes 17 | cache_valid_time: 3600 18 | with_items: 19 | - erlang 20 | - elixir 21 | -------------------------------------------------------------------------------- /roles/install_mysql/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install MySQL 3 | apt: 4 | pkg: "{{ item }}" 5 | state: present 6 | update_cache: yes 7 | cache_valid_time: 3600 8 | with_items: 9 | - mysql-server 10 | - mysql-client 11 | - python-mysqldb 12 | - libmysqlclient-dev 13 | -------------------------------------------------------------------------------- /roles/install_nginx/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install nginx 3 | apt: 4 | pkg: "{{ item }}" 5 | state: present 6 | update_cache: yes 7 | cache_valid_time: 3600 8 | with_items: 9 | - nginx 10 | -------------------------------------------------------------------------------- /roles/install_nodejs/0.0.1/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | distro: xenial 3 | version: node_7.x 4 | -------------------------------------------------------------------------------- /roles/install_nodejs/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add an apt nodesource signing key 3 | apt_key: 4 | url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key 5 | state: present 6 | 7 | - name: add nodesource repository into sources list 8 | apt_repository: 9 | repo: "{{ item }}" 10 | state: present 11 | with_items: 12 | - deb https://deb.nodesource.com/{{ version }} {{ distro }} main 13 | - deb-src https://deb.nodesource.com/{{ version }} {{ distro }} main 14 | 15 | - name: install packages 16 | apt: 17 | pkg: "{{ item }}" 18 | state: present 19 | update_cache: yes 20 | cache_valid_time: 3600 21 | with_items: 22 | - nodejs 23 | -------------------------------------------------------------------------------- /roles/install_postgresql/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: install PostgreSQL 3 | apt: 4 | pkg: "{{ item }}" 5 | state: present 6 | update_cache: yes 7 | cache_valid_time: 3600 8 | with_items: 9 | - postgresql 10 | - python-psycopg2 11 | - postgresql-contrib 12 | - postgresql-server-dev-9.5 13 | -------------------------------------------------------------------------------- /roles/install_yarn/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: add an apt yarnpkg signing key 3 | apt_key: 4 | url: https://dl.yarnpkg.com/debian/pubkey.gpg 5 | state: present 6 | 7 | - name: add yarn repository into sources list 8 | apt_repository: 9 | repo: "{{ item }}" 10 | state: present 11 | with_items: 12 | - deb https://dl.yarnpkg.com/debian/ stable main 13 | 14 | - name: install packages 15 | apt: 16 | pkg: "{{ item }}" 17 | state: present 18 | update_cache: yes 19 | cache_valid_time: 3600 20 | with_items: 21 | - yarn 22 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | lets_encrypt_contact_email: admin@lunarlogic.io 3 | lets_encrypt_environment: production 4 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reload nginx 3 | service: name=nginx state=restarted 4 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create SSL directory" 3 | file: dest=/etc/nginx/ssl state=directory 4 | 5 | - name: "Generate DH params" 6 | command: openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048 creates=/etc/nginx/ssl/dhparams.pem 7 | notify: reload nginx 8 | 9 | 10 | - name: "Install certbot" 11 | command: "wget https://dl.eff.org/certbot-auto" 12 | args: 13 | creates: "/root/certbot-auto" 14 | chdir: "/root/" 15 | 16 | - name: "Make certbot executable" 17 | file: 18 | path: /root/certbot-auto 19 | mode: 0755 20 | 21 | - name: "Create let's encrypt directory" 22 | file: dest=/etc/letsencrypt state=directory 23 | 24 | - name: "Create webroot directory" 25 | file: dest=/var/www state=directory 26 | 27 | - name: "Configure let's encrypt" 28 | template: 29 | src: "{{ item }}.j2" 30 | dest: "/etc/letsencrypt/{{ item }}" 31 | with_items: 32 | - cli.ini 33 | 34 | - name: "Configure nginx well-know snippet" 35 | template: 36 | src: "{{ item }}.j2" 37 | dest: "/etc/nginx/snippets/{{ app_name }}/{{ item }}" 38 | with_items: 39 | - well-known.conf 40 | notify: reload nginx 41 | 42 | - meta: flush_handlers 43 | 44 | - name: "Attempt to obtain SSL certificate using the webroot authenticator" 45 | command: > 46 | /root/certbot-auto 47 | --rsa-key-size 4096 48 | --agree-tos {% for d in domains %}-d {{d}} {% endfor %} 49 | --expand 50 | --text 51 | --non-interactive 52 | -a webroot 53 | --webroot-path /var/www/ 54 | auth 55 | args: 56 | creates: "/etc/letsencrypt/live/{{ domains[0] }}" 57 | 58 | - name: "Configure nginx snippets" 59 | template: 60 | src: "{{ item }}.j2" 61 | dest: "/etc/nginx/snippets/{{ app_name }}/{{ item }}" 62 | with_items: 63 | - ssl-params.conf 64 | notify: reload nginx 65 | 66 | - name: Fix the renewal file 67 | ini_file: section=renewalparams option=authenticator value=webroot dest="/etc/letsencrypt/renewal/{{ domains[0] }}.conf" 68 | 69 | - name: "Schedule certificates renewals" 70 | cron: 71 | name: "SSL certificates renewal" 72 | minute: 0 73 | hour: 0 74 | state: present 75 | job: /root/certbot-auto renew --quiet --no-self-upgrade --renew-hook "sudo service nginx restart" 76 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/templates/cli.ini.j2: -------------------------------------------------------------------------------- 1 | {% if lets_encrypt_environment == "production" %} 2 | server = https://acme-v01.api.letsencrypt.org/directory 3 | {% else %} 4 | server = https://acme-staging.api.letsencrypt.org/directory 5 | {% endif %} 6 | email = {{ lets_encrypt_contact_email }} 7 | webroot-path = /var/www 8 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/templates/ssl-params.conf.j2: -------------------------------------------------------------------------------- 1 | ssl_certificate /etc/letsencrypt/live/{{ domains[0] }}/fullchain.pem; 2 | ssl_certificate_key /etc/letsencrypt/live/{{ domains[0] }}/privkey.pem; 3 | 4 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 5 | ssl_prefer_server_ciphers on; 6 | ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; 7 | ssl_ecdh_curve secp384r1; 8 | ssl_session_cache shared:SSL:10m; 9 | ssl_session_timeout 1h; 10 | ssl_session_tickets off; 11 | ssl_stapling on; 12 | ssl_stapling_verify on; 13 | resolver 8.8.8.8 8.8.4.4 valid=300s; 14 | resolver_timeout 5s; 15 | # add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"; 16 | add_header X-Frame-Options DENY; 17 | add_header X-Content-Type-Options nosniff; 18 | ssl_dhparam /etc/nginx/ssl/dhparams.pem; 19 | -------------------------------------------------------------------------------- /roles/lets_encrypt/0.0.1/templates/well-known.conf.j2: -------------------------------------------------------------------------------- 1 | location /.well-known/acme-challenge { 2 | root /var/www; 3 | } 4 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/README.md: -------------------------------------------------------------------------------- 1 | # phoenix-app 2 | 3 | ## Phoenix Behind a Proxy 4 | 5 | http://www.phoenixframework.org/docs/serving-your-application-behind-a-proxy 6 | 7 | We use nginx behind proxy in order to: 8 | 9 | * serve static files via nginx 10 | * have option to route part of the traffic to other apps than phoenix 11 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: reload nginx 3 | service: name=nginx state=restarted 4 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/tasks/app_server.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create app server service in systemd 3 | template: 4 | src: app_name.service.j2 5 | dest: /etc/systemd/system/{{ app_name }}.service 6 | mode: 0644 7 | 8 | - name: enable app service in systemd 9 | systemd: 10 | name: "{{ app_name }}" 11 | enabled: yes 12 | daemon_reload: yes 13 | state: restarted 14 | 15 | - name: allow user to restart the application 16 | lineinfile: 17 | dest: /etc/sudoers 18 | state: present 19 | line: "{{ username }} ALL=(ALL) NOPASSWD: /bin/systemctl restart {{ app_name }}" 20 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/tasks/configure_nginx.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Create nginx snippets directory for the app" 3 | file: dest="/etc/nginx/snippets/{{ app_name }}" state="directory" 4 | 5 | - name: configure app site 6 | template: 7 | src: nginx.j2 8 | dest: /etc/nginx/sites-available/{{ app_name }} 9 | owner: root 10 | group: root 11 | backup: true 12 | notify: reload nginx 13 | 14 | - name: activate app site configuration 15 | file: 16 | path: /etc/nginx/sites-enabled/{{ app_name }} 17 | src: /etc/nginx/sites-available/{{ app_name }} 18 | state: link 19 | notify: reload nginx 20 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: app_server.yml 3 | - include: configure_nginx.yml 4 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/templates/app_name.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Server for {{ app_name }} 3 | Wants=postgresql.service 4 | After=postgresql.service 5 | After=syslog.target 6 | After=network.target 7 | 8 | [Service] 9 | Type=simple 10 | User={{ username }} 11 | Group={{ username }} 12 | WorkingDirectory=/home/{{ username }}/{{ app_name }}/ 13 | ExecStart=/home/{{ username }}/{{ app_name }}/bin/{{ app_name }} foreground 14 | KillMode=process 15 | Restart=on-failure 16 | SuccessExitStatus=143 17 | TimeoutSec=10 18 | RestartSec=5 19 | SyslogIdentifier={{ app_name }} 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | -------------------------------------------------------------------------------- /roles/phoenix-app/0.0.1/templates/nginx.j2: -------------------------------------------------------------------------------- 1 | # Based on: 2 | # http://www.phoenixframework.org/docs/serving-your-application-behind-a-proxy 3 | 4 | upstream {{ app_name }} { 5 | server 127.0.0.1:8888 max_fails=5 fail_timeout=60s; 6 | } 7 | 8 | server { 9 | listen 80; 10 | server_name {{ domains | join(" ") }}; 11 | 12 | # allows Let's Encrypt verification request on HTTP 13 | location ~ /.well-known { 14 | root /var/www; 15 | allow all; 16 | } 17 | 18 | location / { 19 | return 301 https://{{ domains[0] }}$request_uri; 20 | } 21 | } 22 | 23 | server { 24 | listen 443 ssl; 25 | 26 | server_name {{ domains[0] }}; 27 | 28 | client_max_body_size 999M; 29 | 30 | gzip on; 31 | gzip_types text/plain text/css text/xml application/x-javascript application/xml application/rss+xml 32 | application/atom+xml text/javascript application/javascript application/json text/mathml image/svg+xml; 33 | 34 | include "snippets/{{ app_name }}/*.conf"; 35 | 36 | location / { 37 | allow all; 38 | 39 | # Proxy Headers 40 | proxy_http_version 1.1; 41 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 42 | proxy_set_header Host $http_host; 43 | proxy_set_header X-Cluster-Client-Ip $remote_addr; 44 | 45 | # The Important Websocket Bits! 46 | proxy_set_header Upgrade $http_upgrade; 47 | proxy_set_header Connection "upgrade"; 48 | 49 | proxy_pass http://{{ app_name }}; 50 | } 51 | } 52 | 53 | server { 54 | listen 443 ssl; 55 | server_name {{ domains | join(" ") }}; 56 | 57 | include "snippets/{{ app_name }}/*.conf"; 58 | 59 | location / { 60 | return 301 https://{{ domains[0] }}$request_uri; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /roles/provision_log/0.0.1/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | provision_date: "{{ lookup('pipe', 'date') }}" 3 | provision_version: "{{ lookup('pipe', 'git rev-parse HEAD') }}" 4 | -------------------------------------------------------------------------------- /roles/provision_log/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Log provisioning time and commit 2 | lineinfile: 3 | create: yes 4 | state: present 5 | mode: 0644 6 | line: '{{ provision_date }} {{ provision_version }}' 7 | dest: /var/log/provision.log 8 | -------------------------------------------------------------------------------- /roles/user/0.0.1/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudoer: False 3 | -------------------------------------------------------------------------------- /roles/user/0.0.1/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: create user 3 | user: 4 | name: "{{ username }}" 5 | state: present 6 | shell: /bin/bash 7 | 8 | - name: set authorized keys 9 | authorized_key: 10 | user: "{{ username }}" 11 | state: present 12 | key: "{{ lookup('pipe', 'cat {{ authorized_key_paths | join(\" \") }}') }}" 13 | exclusive: True 14 | 15 | - name: "{{ sudoer | ternary('allow','diallow') }} user to run all commands using sudo without a password" 16 | lineinfile: 17 | dest: /etc/sudoers 18 | state: "{{ sudoer | ternary('present','absent') }}" 19 | line: "{{ username }} ALL=(ALL) NOPASSWD: ALL" 20 | --------------------------------------------------------------------------------