Skip to content

Commit

Permalink
WIP adding missing pieces for oonidata host deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
hellais committed Sep 26, 2024
1 parent 99cd52d commit 356c820
Show file tree
Hide file tree
Showing 22 changed files with 316 additions and 87 deletions.
2 changes: 2 additions & 0 deletions ansible/requirements.yml
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
- src: willshersystems.sshd
- src: nginxinc.nginx
- src: geerlingguy.nginx
76 changes: 0 additions & 76 deletions ansible/roles/jupyterhub/tasks/main.yml

This file was deleted.

2 changes: 0 additions & 2 deletions ansible/roles/jupyterhub/templates/jupyterhub_config.py.j2

This file was deleted.

8 changes: 0 additions & 8 deletions ansible/roles/jupyterhub/vars/main.yml

This file was deleted.

24 changes: 24 additions & 0 deletions ansible/roles/miniconda/tasks/install.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
- name: Ensure miniconda directory exists
ansible.builtin.file:
path: "{{ miniconda_install_dir }}"
state: directory
owner: miniconda
group: "{{ admin_group }}"

- name: Download the miniconda installer
ansible.builtin.get_url:
url: "https://repo.anaconda.com/miniconda/Miniconda3-py312_24.7.1-0-Linux-x86_64.sh"
dest: "{{ miniconda_install_dir }}/miniconda.sh"
checksum: "sha256:33442cd3813df33dcbb4a932b938ee95398be98344dff4c30f7e757cd2110e4f"
mode: "0700"

- name: Run the miniconda installer
become_user: miniconda
ansible.builtin.shell: |
bash {{ miniconda_install_dir }}/miniconda.sh -b -u -p {{ miniconda_install_dir }}
- name: Delete installer
ansible.builtin.file:
path: "{{ miniconda_install_dir }}/miniconda.sh"
state: absent
23 changes: 23 additions & 0 deletions ansible/roles/miniconda/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
- name: Check if Miniconda is installed
ansible.builtin.stat:
path: "{{ miniconda_install_dir }}/bin/conda"
register: miniconda_bin

- name: Create miniconda user
become: yes
ansible.builtin.user:
name: miniconda
create_home: no

- include_tasks: install.yml
become: yes
when: not miniconda_bin.stat.exists

- name: "install conda packages"
ansible.builtin.shell:
cmd: "{{ miniconda_install_dir }}/bin/conda install {{ item }}"
loop:
- pandas
- numpy
- altair
1 change: 1 addition & 0 deletions ansible/roles/miniconda/vars/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
miniconda_install_dir: /opt/miniconda
8 changes: 8 additions & 0 deletions ansible/roles/nginx/files/ffdhe2048_dhparam.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
3 changes: 3 additions & 0 deletions ansible/roles/nginx/files/ssl_intermediate.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Oldest compatible clients: Firefox 1, Chrome 1, IE 7, Opera 5, Safari 1, Windows XP IE8, Android 2.3, Java 7
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
4 changes: 4 additions & 0 deletions ansible/roles/nginx/files/ssl_modern.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Oldest compatible clients: Firefox 27, Chrome 30, IE 11 on Windows 7, Edge, Opera 17, Safari 9, Android 5.0, and Java 8
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
# NB: technically, it does not require ssl_dhparam as it has no DHE, only ECDHE.
15 changes: 15 additions & 0 deletions ansible/roles/nginx/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
- name: test nginx config
command: /usr/sbin/nginx -t -c /etc/nginx/nginx.conf
listen:
- restart nginx
- reload nginx

- name: restart nginx
service:
name: nginx
state: restarted

- name: reload nginx
service:
name: nginx
state: reloaded
4 changes: 4 additions & 0 deletions ansible/roles/nginx/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
dependencies:
- libhandlers
...
17 changes: 17 additions & 0 deletions ansible/roles/nginx/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
- name: install nginx
include_role:
name: nginxinc.nginx

- name: remove `default` vhost
file: path={{item}} state=absent
notify: reload nginx
with_items:
- /etc/nginx/conf.d/default.conf
- /etc/nginx/sites-available/default
- /etc/nginx/sites-enabled/default

- name: set nginx.conf
template: src=nginx.conf dest=/etc/nginx/nginx.conf mode=0444
notify: reload nginx
...
125 changes: 125 additions & 0 deletions ansible/roles/nginx/templates/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# NB: system nginx uses `www-data` user!
user nginx;
worker_processes 2;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# $request_id is 1.11.0+, so `have_nginx` is adequate.
geo $is_ooni {
{% for h in groups['have_nginx'] %}
{{ lookup('dig', h + './A') }} 1;
{% endfor %}
default 0;
}

map $http_x_request_id $has_request_id { # check for `X-Request-ID`
"" 0;
default 1;
}

map "$is_ooni:$has_request_id" $ooni_request_id {
"1:1" $http_x_request_id; # use `X-Request-ID` if it's okay
default $request_id;
}

# IPv4 is anonymized to /24, IPv6 to /48 - according to OONI Data Policy.
# https://ooni.torproject.org/about/data-policy/
# IP is recorded to track possible abusers, not to distinguish users, so the
# address is truncated down to ISP (min routable prefix) instead of hashing.
map $remote_addr $ooni_remote_addr {
default "0.0.0.0";
# variables in map value require nginx/1.11.0+
"~(?P<ip>\d+\.\d+\.\d+)\.\d+" "$ip.0";
# :: means at least TWO zero 16bit fields, https://tools.ietf.org/html/rfc5952#section-4.2.2
"~(?P<ip>[0-9a-f]+:[0-9a-f]+:[0-9a-f]+):[0-9a-f:]+" "$ip::";
"~(?P<ip>[0-9a-f]+:[0-9a-f]+)::[0-9a-f:]+" "$ip::";
"~(?P<ip>[0-9a-f]+)::[0-9a-f:]+" "$ip::";
}

# $server_name is important as mtail does not distinguish log lines from
# different files, $host is required to log actual `Host` header.
# $request is split into separate fields to ease awk and mtail parsing.
# $scheme is used instead of $https to ease eye-reading.
# TCP_INFO is logged for random fun.
log_format mtail_pub
'$time_iso8601\t$msec\t$server_name\t'
'$ooni_remote_addr\t' # pub/int diff
'$request_completion\t$request_time\t$status\t$bytes_sent\t$body_bytes_sent\t'
'$upstream_cache_status\t$upstream_addr\t$upstream_status\t$upstream_connect_time\t$upstream_header_time\t$upstream_response_time\t'
'$scheme\t$server_protocol\t$request_length\t$request_method\t$host\t$request_uri\t'
'$tcpinfo_rtt\t$tcpinfo_rttvar\t'
'$http_referer\t$http_user_agent\t$ooni_request_id';

log_format mtail_int
'$time_iso8601\t$msec\t$server_name\t'
'$remote_addr\t' # pub/int diff
'$request_completion\t$request_time\t$status\t$bytes_sent\t$body_bytes_sent\t'
'$upstream_cache_status\t$upstream_addr\t$upstream_status\t$upstream_connect_time\t$upstream_header_time\t$upstream_response_time\t'
'$scheme\t$server_protocol\t$request_length\t$request_method\t$host\t$request_uri\t'
'$tcpinfo_rtt\t$tcpinfo_rttvar\t'
'$http_referer\t$http_user_agent\t$ooni_request_id';

log_format oolog '$ooni_remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" "$host"';

log_format oolog_mtail '$time_iso8601\t$msec\t$server_name\t'
'$ooni_remote_addr\t' # pub/int diff
'$request_completion\t$request_time\t$status\t$bytes_sent\t$body_bytes_sent\t'
'$upstream_cache_status\t$upstream_addr\t$upstream_status\t$upstream_connect_time\t$upstream_header_time\t$upstream_response_time\t'
'$scheme\t$server_protocol\t$request_length\t$request_method\t$host\t$request_uri\t'
'$tcpinfo_rtt\t$tcpinfo_rttvar\t'
'$http_referer\t$http_user_agent\t$ooni_request_id';

access_log /var/log/nginx/access.log mtail_int;

sendfile on;
tcp_nopush on; # TCP_CORK HTTP headers with sendfile() body into single packet

keepalive_timeout 120 120; # Firefox has 115s, http://kb.mozillazine.org/Network.http.keep-alive.timeout

server_tokens off;

# SSL based on https://wiki.mozilla.org/Security/Server_Side_TLS (doc v4.1)
ssl_session_timeout 1d;
ssl_session_cache shared:GLOBAL:1m; # 1m of cache is ~4000 sessions
ssl_session_tickets off; # needs accurate key rotation
ssl_dhparam /etc/nginx/ffdhe2048_dhparam.pem; # https://tools.ietf.org/html/rfc7919
ssl_prefer_server_ciphers on;
#TODO: ssl_stapling on; # needs `resolver` or `ssl_stapling_file`
#TODO: ssl_stapling_verify on; # needs `ssl_trusted_certificate`
#TODO: resolver <IP DNS resolver>;
# Define in server{}
# - include /etc/nginx/ssl_modern.conf | /etc/nginx/ssl_intermediate.conf
# - ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem;
# - ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem
# - ssl_trusted_certificate /etc/letsencrypt/live/example.org/chain.pem; # for ssl_stapling_verify
# - add_header Strict-Transport-Security max-age=15768000; # HSTS (15768000 seconds = 6 months)
###

gzip on;
gzip_types text/html text/plain text/css text/xml text/javascript application/x-javascript application/json application/xml; # default is only `text/html`
gzip_disable "msie6";
#gzip_proxied any;

# Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto are from
# file /etc/nginx/proxy_params from nginx-common package
# NB: adding `proxy_set_header` in another location overwrites whole set!
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID $ooni_request_id;

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
5 changes: 5 additions & 0 deletions ansible/roles/oonidata/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- name: Restart jupyterhub
ansible.builtin.systemd_service:
name: jupyterhub
state: restarted
daemon_reload: true
5 changes: 5 additions & 0 deletions ansible/roles/oonidata/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dependencies:
- role: miniconda
vars:
miniconda_install_dir: "{{ miniconda_install_dir }}"
- role: nginx
42 changes: 42 additions & 0 deletions ansible/roles/oonidata/tasks/jupyterhub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
- name: "install jupyterhub"
become: yes
become_user: miniconda
ansible.builtin.shell:
cmd: "{{ miniconda_install_dir }}/bin/conda install -c conda-forge jupyterhub"

- name: "install jupyterlab and notebook"
become: yes
become_user: miniconda
ansible.builtin.shell:
cmd: "{{ miniconda_install_dir }}/bin/conda install jupyterlab notebook"

- name: Write jupyterhub config
ansible.builtin.template:
src: jupyterhub_config.py.j2
dest: "{{ jupyterhub_config_dir }}/config.py"
owner: root
group: {{ admin_group }}
mode: "0640"
notify:
- Restart jupyterhub

- name: Write jupyterhub service
ansible.builtin.template:
src: jupyterhub.service.j2
dest: "/etc/systemd/system/jupyterhub.service"
owner: root
group: root
mode: "0644"
notify:
- Restart jupyterhub

- name: Ensure the JupyterHub service is started with daemon-reload
become: true
tags:
- config
ansible.builtin.systemd:
name: jupyterhub
state: started
enabled: true
daemon_reload: true
12 changes: 12 additions & 0 deletions ansible/roles/oonidata/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
- include_tasks: jupyterhub.yml
become: yes

- include_role: geerlingguy.nginx
vars:
certbot_admin_email: [email protected]
certbot_create_if_missing: true
certbot_create_standalone_stop_services: []
certbot_certs:
- domains:
- {{ inventory_name }}
Loading

0 comments on commit 356c820

Please sign in to comment.