Moving CTFd to eqy.fr

This commit is contained in:
Julien Palard 2022-09-24 17:29:27 +02:00
parent 3f8652723a
commit c370d57c7a
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
9 changed files with 362 additions and 4 deletions

141
ctfd.yml
View File

@ -1,8 +1,141 @@
---
- hosts: mdk
roles: ["julienpalard.ctfd"]
- hosts: ctfd
vars:
domain: ctf.mdk.fr
domain: ctf.eqy.fr
owner: ctfd
version: master
home: "/home/ctfd"
letsencrypt_email: julien@palard.fr
secret_key: "{{ vault_ctfd_secret }}"
secret_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
34396134346435343464653766663833643061666164323337646137636631643930326633333239
3433333563366461646665643739383466343465663733650a326533316138366336333231616162
62623562346561663936303861363863626336343437333164343063323533353432653766356334
6138343864666637660a383165356630363533376562323663353636373636613035636339626631
31643062353434333534333130636237396365633662343964666134333833373439363833323062
3032666163643162613766306437356438653538333163346531
tasks:
- name: Create user
user:
name: "{{ owner }}"
home: "{{ home }}"
- name: Clone ctfd
git:
repo: https://github.com/CTFd/CTFd
dest: "{{ home }}/CTFd/"
become: true
become_user: "{{ owner }}"
- name: Setup secret key
copy:
content: "{{ secret_key }}"
dest: "{{ home }}/CTFd/.ctfd_secret_key"
- name: Configure nginx
include_role: name=nginx
vars:
nginx_domain: "{{ domain }}"
nginx_certificates:
- "{{ domain }}"
nginx_owner: "{{ owner }}"
nginx_conf: |
server
{
listen 80;
server_name {{ domain }};
access_log /var/log/nginx/{{ domain }}-access.log;
error_log /var/log/nginx/{{ domain }}-error.log;
return 301 https://$host$request_uri;
}
server
{
listen 443 ssl;
server_name {{ domain }};
access_log /var/log/nginx/{{ domain }}-access.log;
error_log /var/log/nginx/{{ domain }}-error.log;
include snippets/letsencrypt-{{ domain }}.conf;
add_header X-Frame-Options DENY;
charset utf-8;
location /
{
proxy_pass http://unix:{{ home }}/ctfd.sock;
proxy_redirect off;
proxy_set_header Host $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;
}
}
- name: Install requirements
pip:
requirements: "{{ home }}/CTFd/requirements.txt"
virtualenv_command: "/usr/bin/python3 -m venv"
virtualenv: "{{ home }}/venv/"
become: true
become_user: "{{ owner }}"
- name: Install MariaDB
# CTFd can run on SQLite but with migration issues
# See #1988.
package:
state: present
name:
- mariadb-server
- python3-pymysql
- name: MariaDB database
community.mysql.mysql_db:
name: ctfd
state: present
login_unix_socket: /run/mysqld/mysqld.sock
- name: MariaDB user
community.mysql.mysql_user:
state: present
name: ctfd
priv: 'ctfd.*:ALL'
login_unix_socket: /run/mysqld/mysqld.sock
- name: Configure CTFd to use MariaDB
lineinfile:
path: '/home/ctfd/CTFd/CTFd/config.ini'
regex: '^DATABASE_URL'
line: 'DATABASE_URL = mysql+pymysql://ctfd@/ctfd?unix_socket=/run/mysqld/mysqld.sock'
notify: Restart CTFd
- name: Configure systemd
copy:
dest: "/etc/systemd/system/{{ domain }}.service"
content: |
[Unit]
Description=CTFd ({{ domain }})
After=network.target
[Service]
PIDFile={{ home }}/gunicorn.pid
User={{ owner }}
Group={{ owner }}
RuntimeDirectory=pasteque
WorkingDirectory={{ home }}/CTFd/
ExecStart={{ home }}/venv/bin/gunicorn --worker-class gevent -w6 -t 120 --pid {{ home }}/gunicorn.pid \
--bind unix:{{ home }}/ctfd.sock wsgi:app
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
- name: Start CTFd
service: name="{{ domain }}" enabled=yes state=started daemon_reload=yes
handlers:
- name: Restart CTFd
service: name=ctf.eqy.fr state=restarted

4
group_vars/all/vars.yml Normal file
View File

@ -0,0 +1,4 @@
---
admin_email: julien@palard.fr
gandi_api_key: "{{ vault_gandi_api_key }}"

8
group_vars/all/vault.yml Normal file
View File

@ -0,0 +1,8 @@
$ANSIBLE_VAULT;1.1;AES256
61333239366432343130623339303039336432376630396530656333646231346230383066646137
6137386633646634653864366235613435633033653961330a616130353838333863346536653565
31666161386231353038343066633533323866326532343832666135366537393966366461653763
3864646630653463610a373063346431393464383739643336663239623533626530653332623631
64396532656633363236633461323361313833373538373261386530653339313235633562336638
62643237623933663732366533363635616339646265633961333037393438373231613364623939
636464383531656363306435643365313765

View File

@ -3,6 +3,10 @@ all:
ansible_user: root
ansible_python_interpreter: /usr/bin/python3
children:
ctfd:
hosts:
eqy.fr:
ansible_host: 163.172.82.132
mdk:
hosts:
mdk.fr:

29
roles/nginx/README.md Normal file
View File

@ -0,0 +1,29 @@
# Nginx with Letsencrypt
This role sets up nginx with letsencrypt (using DNS-01 with Gandi API) .
## Role Variables
The mandatory variables are:
- `admin_email`: For letsencrypt.
- `gandi_api_key` ([see doc](https://github.com/obynio/certbot-plugin-gandi/)).
- `nginx_certificates`: A list of domain to put in this certificate.
- `nginx_domain`: Used for file names, certificate name, and default server_name if no nginx_conf is given.
- `nginx_conf`: The nginx config.
Optional variables are:
- `nginx_owner`: If a unix user has to be created for this project.
- `nginx_path`: To create a directory owned by `nginx_owner`.
- `ssl_ciphers`: [Specifies the enabled ciphers](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_ciphers).
- `ssl_protocols`: [Enables the specified protocols.](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols)
- `ssl_prefer_server_ciphers`: [Specifies that server ciphers should be preferred over client ciphers when using the SSLv3 and TLS protocols.](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_prefer_server_ciphers)
- `ssl_session_cache`: [Sets the types and sizes of caches that store session parameters.](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache).
- `HSTS_header`: HTTP header to inject.
### Author Information
Julien Palard — https://mdk.fr

View File

@ -0,0 +1,29 @@
---
ssl_ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
ssl_protocols: "TLSv1.2 TLSv1.3"
ssl_prefer_server_ciphers: "off"
ssl_session_cache: "shared:ssl_session_cache:10m"
HSTS_header: 'Strict-Transport-Security "max-age=63072000; always"'
nginx_conf: |
server
{
listen [::]:80; listen 80;
server_name {{ nginx_domain }};
access_log /var/log/nginx/{{ nginx_domain }}-access.log;
error_log /var/log/nginx/{{ nginx_domain }}-error.log;
return 301 https://$host$request_uri;
}
server
{
listen [::]:443 ssl; listen 443 ssl;
charset utf-8;
server_name {{ nginx_domain }};
access_log /var/log/nginx/{{ nginx_domain }}-access.log;
error_log /var/log/nginx/{{ nginx_domain }}-error.log;
include snippets/letsencrypt-{{ nginx_domain }}.conf;
root {{ nginx_path }};
index index.html;
}

View File

@ -0,0 +1,6 @@
---
- name: reload nginx
service:
name: nginx
state: reloaded

124
roles/nginx/tasks/main.yml Normal file
View File

@ -0,0 +1,124 @@
---
- name: Create SSL dhparam
get_url:
url: https://ssl-config.mozilla.org/ffdhe2048.txt
dest: /etc/ssl/certs/dhparam.pem
mode: 0644
- name: Prepare certbot+gandi venv
pip:
chdir: /root/
virtualenv_command: /usr/bin/python3 -m venv
virtualenv: /root/certbot-venv/
name:
- "pip>=21.0.1"
- "setuptools>=53.0.0"
- "wheel>=0.36.2"
- name: Install certbot+gandi in venv
pip:
chdir: /root/
virtualenv_command: /usr/bin/python3 -m venv
virtualenv: /root/certbot-venv/
name:
- certbot
- certbot-plugin-gandi
- name: Setup Gandi credentials
copy:
content: |
dns_gandi_api_key = {{ gandi_api_key }}
mode: 0600
dest: /root/gandi.ini
- name: Generate TLS certificates
command: /root/certbot-venv/bin/certbot certonly --cert-name {{ nginx_domain | quote }} -n --agree-tos -d {{ nginx_certificates | join(",") | quote }} -m {{ admin_email | quote }} --authenticator dns-gandi --dns-gandi-credentials /root/gandi.ini
register: certbot
changed_when: '"no action taken." not in certbot.stdout'
- name: Setup renewal cron
cron:
name: certbot
minute: "55"
hour: "8"
job: '/root/certbot-venv/bin/certbot -q renew'
- name: Setup PATH in renewal cron
ansible.builtin.cron:
name: PATH
env: yes
job: /usr/sbin
- name: Setup renewal hook directory
file:
path: /etc/letsencrypt/renewal-hooks/post
state: directory
mode: 0755
- name: Setup renewal hook script
copy:
dest: /etc/letsencrypt/renewal-hooks/deploy/nginx.sh
mode: 0755
content: |
#!/bin/sh
/usr/bin/systemctl reload nginx
- name: Install nginx
package:
state: present
name:
- nginx
- ca-certificates
- name: Ensure certbot is not installed from Debian packages
package:
state: absent
name:
- certbot
- python-certbot-nginx
- python3-certbot-nginx
- name: Create letsencrypt snippets
template:
src: letsencrypt.conf.j2
dest: '/etc/nginx/snippets/letsencrypt-{{ nginx_domain }}.conf'
- name: User
user:
system: true
name: "{{ nginx_owner }}"
when: nginx_owner is defined
- name: .ssh directory
file:
path: "~{{ nginx_owner }}/.ssh"
state: directory
owner: "{{ nginx_owner }}"
mode: 0755
when: nginx_owner is defined
- name: Deploy key
blockinfile:
create: true
owner: "{{ nginx_owner }}"
mode: 0644
path: "~{{ nginx_owner }}/.ssh/authorized_keys"
marker: "<!-- {mark} ANSIBLE MANAGED BLOCK: Deploy key for {{ nginx_domain }} -->"
block: "{{ nginx_public_deploy_key }}"
when: nginx_owner is defined and nginx_public_deploy_key is defined
- name: Configure nginx
copy:
content: "{{ nginx_conf }}"
dest: "/etc/nginx/conf.d/{{ nginx_domain }}.conf"
notify: reload nginx
- name: WWW directory
file:
path: "{{ nginx_path }}"
state: directory
owner: "{{ nginx_owner }}"
group: "{{ nginx_owner }}"
mode: 0755
when: nginx_owner is defined and nginx_path is defined

View File

@ -0,0 +1,21 @@
# Inspired from
# https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=intermediate&openssl=1.1.1d&guideline=5.6
ssl_ciphers "{{ ssl_ciphers }}";
ssl_protocols {{ ssl_protocols }};
ssl_prefer_server_ciphers {{ ssl_prefer_server_ciphers }};
ssl_session_cache {{ ssl_session_cache }};
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_certificate /etc/letsencrypt/live/{{ nginx_domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ nginx_domain }}/privkey.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
{% if HSTS_header %}
add_header {{ HSTS_header }};
{% endif %}