Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions playbooks/docraptor_nginx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
# you MUST run this playbook on a single host with '--limit' for example `ansible-playbook -v --limit adc-prod2.princeton.edu playbooks/docraptor_nginx.yml`
# To find the active load-balancer ssh onto a machine and run `ip a |grep 146` the active one will display `inet 128.112.203.146/32 scope global eno1`
- name: Deploy DocRaptor dynamic allow-list for NGINX Plus
hosts: nginxplus_production # default to staging when we get a staging environment going
remote_user: pulsys
become: true
vars_files:
- ../group_vars/nginxplus/main.yml
- ../group_vars/nginxplus/production.yml # when we merge the playbook this can have the environment

vars:
# URL to fetch the DocRaptor IP list from
docraptor_ip_list_url: "https://docraptor.com/ips.txt"

# Where to drop the NJS script on the webservers
docraptor_js_path: "/etc/nginx/js/docraptor.js"

# How often (in hours) to refresh the list in memory
# (set to 24 for once-a-day)
refresh_interval_hours: 24

tasks:
- name: Ensure NJS module directory exists
ansible.builtin.file:
path: "{{ docraptor_js_path | dirname }}"
state: directory
owner: root
group: root
mode: '0755'

- name: Deploy DocRaptor JS access filter
# Write out the NGINX Plus JavaScript that:
# • pulls docraptor_ip_list_url
# • splits it into an array
# • refreshes every refresh_interval_hours
# • implements an access-phase check
ansible.builtin.copy:
dest: "{{ docraptor_js_path }}"
owner: root
group: root
mode: '0644'
content: |
// Auto-generated by Ansible: DocRaptor dynamic allow-list
var ips = [];

// Fetch and parse the remote IP list
function refresh() {
fetch('{{ docraptor_ip_list_url }}', { method: 'GET' })
.then(function(r) { return r.text(); })
.then(function(t) {
// split on newlines, drop empties
ips = t.split(/\r?\n/).filter(function(x){ return x; });
console.log('DocRaptor IPs loaded: ' + ips.length);
})
.catch(function(e) {
console.error('DocRaptor fetch error:', e);
});
}

// initial load + daily refresh
refresh();
setInterval(refresh, {{ refresh_interval_hours }} * 60 * 60 * 1000);

// called in the access phase
function check(r) {
if (ips.indexOf(r.remoteAddress) !== -1) {
return; // allow
}
r.return(403); // deny
}

export default { check };

notify: Reload NGINX Plus

handlers:
- name: Reload NGINX Plus
ansible.builtin.systemd:
name: nginx
state: reloaded
post_tasks:
- name: tell everyone on slack you ran an ansible playbook
community.general.slack:
token: "{{ vault_pul_slack_token }}"
msg: "Ansible ran `{{ ansible_play_name }}` on {{ inventory_hostname }}. Honeybadger may log connection errors around this time due to the nginx restart."
channel: "{{ item }}"
loop: "{{ slack_alerts_channel }}"
tags: always