From e0abdbe5061589e4485232e7da229a5ea89410b5 Mon Sep 17 00:00:00 2001 From: Bastian de Byl Date: Thu, 24 Sep 2020 21:06:56 -0400 Subject: [PATCH] Initial working commit --- .git-crypt/.gitattributes | 4 ++ ...A4AA02555DBD559189B4E0F32BE05EADAA54FC.gpg | Bin 0 -> 725 bytes .gitattributes | 1 + .gitignore | 1 + .pass.sh | Bin 0 -> 65 bytes Makefile | 44 +++++++++++++ ansible.cfg | 11 ++++ ansible/deploy.yml | 2 + ansible/deploy_home.yml | 5 ++ ansible/inventories/home/hosts.yml | 5 ++ ansible/roles/common/defaults/main.yml | 10 +++ .../common/files/fail2ban/jails/nginx.local | 20 ++++++ .../common/files/fail2ban/jails/sshd.local | 10 +++ ansible/roles/common/handlers/main.yml | 12 ++++ ansible/roles/common/tasks/deps.yml | 7 +++ ansible/roles/common/tasks/main.yml | 3 + ansible/roles/common/tasks/security.yml | 31 +++++++++ ansible/roles/drone/meta/main.yml | 3 + ansible/roles/drone/tasks/docker.yml | 17 +++++ ansible/roles/drone/tasks/main.yml | 2 + ansible/roles/http/defaults/main.yml | 9 +++ ansible/roles/http/files/nginx/nginx.conf | 36 +++++++++++ ansible/roles/http/handlers/main.yml | 6 ++ ansible/roles/http/meta/main.yml | 3 + ansible/roles/http/tasks/cron.yml | 10 +++ ansible/roles/http/tasks/deps.yml | 7 +++ ansible/roles/http/tasks/http.yml | 56 +++++++++++++++++ ansible/roles/http/tasks/main.yml | 4 ++ ansible/roles/http/tasks/ssl.yml | 59 ++++++++++++++++++ .../nginx/sites/ci.bdebyl.net.http.conf.j2 | 14 +++++ .../nginx/sites/ci.bdebyl.net.https.conf.j2 | 35 +++++++++++ requirements.txt | 3 + 32 files changed, 430 insertions(+) create mode 100644 .git-crypt/.gitattributes create mode 100644 .git-crypt/keys/default/0/70A4AA02555DBD559189B4E0F32BE05EADAA54FC.gpg create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100755 .pass.sh create mode 100644 Makefile create mode 100644 ansible.cfg create mode 100644 ansible/deploy.yml create mode 100644 ansible/deploy_home.yml create mode 100644 ansible/inventories/home/hosts.yml create mode 100644 ansible/roles/common/defaults/main.yml create mode 100644 ansible/roles/common/files/fail2ban/jails/nginx.local create mode 100644 ansible/roles/common/files/fail2ban/jails/sshd.local create mode 100644 ansible/roles/common/handlers/main.yml create mode 100644 ansible/roles/common/tasks/deps.yml create mode 100644 ansible/roles/common/tasks/main.yml create mode 100644 ansible/roles/common/tasks/security.yml create mode 100644 ansible/roles/drone/meta/main.yml create mode 100644 ansible/roles/drone/tasks/docker.yml create mode 100644 ansible/roles/drone/tasks/main.yml create mode 100644 ansible/roles/http/defaults/main.yml create mode 100644 ansible/roles/http/files/nginx/nginx.conf create mode 100644 ansible/roles/http/handlers/main.yml create mode 100644 ansible/roles/http/meta/main.yml create mode 100644 ansible/roles/http/tasks/cron.yml create mode 100644 ansible/roles/http/tasks/deps.yml create mode 100644 ansible/roles/http/tasks/http.yml create mode 100644 ansible/roles/http/tasks/main.yml create mode 100644 ansible/roles/http/tasks/ssl.yml create mode 100644 ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.http.conf.j2 create mode 100644 ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.https.conf.j2 create mode 100644 requirements.txt diff --git a/.git-crypt/.gitattributes b/.git-crypt/.gitattributes new file mode 100644 index 0000000..665b10e --- /dev/null +++ b/.git-crypt/.gitattributes @@ -0,0 +1,4 @@ +# Do not edit this file. To specify the files to encrypt, create your own +# .gitattributes file in the directory where your files are. +* !filter !diff +*.gpg binary diff --git a/.git-crypt/keys/default/0/70A4AA02555DBD559189B4E0F32BE05EADAA54FC.gpg b/.git-crypt/keys/default/0/70A4AA02555DBD559189B4E0F32BE05EADAA54FC.gpg new file mode 100644 index 0000000000000000000000000000000000000000..1f99bc2ae17f5b5b9fd745fea52d3f42eb7528f6 GIT binary patch literal 725 zcmV;`0xJE50t^F!ghmXfFT(Kw5CEoYDVNp80>Iuy;R}q59*hi2#}_ z*rl^}&qqyE!EKQ!k~4-YojjTG1__Bs&ok$Vvg=<@e>rZMl!1n%;EFcCZi+ z4<;`>VR33^Pro2T92>D z5|Vw|9>I1i8+IsgAwGLnO^r2F$XyRZ`!lcFxy3~&o93HPNyL!IIXL(-$nu!&zt)*Q zWp$>_%fTp82~ZF8jrI{@T%;UoIue0odV&{IbQv`*O$JiK^j8|(+km;#FfwYH&m5~; zZmtnyUY{3}>msq$epRLls0|DH6IHY#UsD6Sd0{m!ivGKfs7!vfCP(^vfiT6pPW7X%f zZOR_;xGlJddRhtE(@ED1)7rl~k5oTw5|0*~U_&m+{Ti^=p*d%EcNn{7Lzu8Ad*}@S z+HO#?%g?Vq1_0k_DcquUt@CbAN;!>@@H8!#ZZo&hSMMTey&~>;va{%2OpTAh*ri@A z$cE@t?c!iz=Wevv$X_M|E4;&>&a7`(n^}h|o$O$}tp}?I82fQ>?tqk*T0|?%1``XQ HX;X|qEhl4J literal 0 HcmV?d00001 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1bbbb10 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.pass.sh filter=git-crypt diff=git-crypt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0218305 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.ansible-vaultpass diff --git a/.pass.sh b/.pass.sh new file mode 100755 index 0000000000000000000000000000000000000000..f60665d9b016058864292d055e9975c860a48127 GIT binary patch literal 65 zcmV-H0KWeKM@dveQdv+`0BlD=U`Ln0V_WI{u||ZTxXb#Lv%j^UsxJb^q2U9;nAD;t Xu94piFlTi&*0;AfzJ#UJx{6%i $@ + +# Targets +deploy: ${ANSIBLE} ${VAULT_PASS_FILE} + ${ANSIBLE} --diff --private-key ${SSH_KEY} -t ${TAGS} -i ${ANSIBLE_INVENTORY} --vault-password-file ${VAULT_PASS_FILE} ansible/deploy.yml + +check: ${ANSIBLE} ${VAULT_PASS_FILE} + ${ANSIBLE} --check --diff --private-key ${SSH_KEY} -t ${TAGS} -i ${ANSIBLE_INVENTORY} --vault-password-file ${VAULT_PASS_FILE} ansible/deploy.yml + +encrypt-string: ${ANSIBLE_VAULT} ${VAULT_PASS_FILE} + ${ANSIBLE_VAULT} encrypt_string --vault-password-file ${VAULT_PASS_FILE} + +lint: ${LINT_YAML} ${LINT_ANSIBLE} + @${LINT_YAML} ansible/ + @${LINT_ANSIBLE} ansible/ diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..fa50a29 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,11 @@ +[defaults] +callback_whitelist = profile_tasks + +# Do not gather facts by default +gathering = explicit + +# Hide warnings about discovered Python interpreter +interpreter_python = auto_silent + +[ssh_connection] +pipelining = true diff --git a/ansible/deploy.yml b/ansible/deploy.yml new file mode 100644 index 0000000..90bec91 --- /dev/null +++ b/ansible/deploy.yml @@ -0,0 +1,2 @@ +--- +- import_playbook: deploy_home.yml diff --git a/ansible/deploy_home.yml b/ansible/deploy_home.yml new file mode 100644 index 0000000..f0f1033 --- /dev/null +++ b/ansible/deploy_home.yml @@ -0,0 +1,5 @@ +--- +- hosts: all + roles: + - role: common + - role: http diff --git a/ansible/inventories/home/hosts.yml b/ansible/inventories/home/hosts.yml new file mode 100644 index 0000000..64a403e --- /dev/null +++ b/ansible/inventories/home/hosts.yml @@ -0,0 +1,5 @@ +--- +all: + hosts: + home.bdebyl.net: + ansible_user: ansible diff --git a/ansible/roles/common/defaults/main.yml b/ansible/roles/common/defaults/main.yml new file mode 100644 index 0000000..9b0f71d --- /dev/null +++ b/ansible/roles/common/defaults/main.yml @@ -0,0 +1,10 @@ +--- +deps: [ + docker, + fail2ban +] + +fail2ban_jails: [ + sshd.local, + nginx.local +] diff --git a/ansible/roles/common/files/fail2ban/jails/nginx.local b/ansible/roles/common/files/fail2ban/jails/nginx.local new file mode 100644 index 0000000..585b2e4 --- /dev/null +++ b/ansible/roles/common/files/fail2ban/jails/nginx.local @@ -0,0 +1,20 @@ +[nginx-limit-req] +enabled = true +port = http,https +findtime = 600 +bantime = 1w +maxretry = 8 + +[nginx-http-auth] +enabled = true +port = http,https +logpath = %(nginx_error_log)s +bantime = 2w +maxretry = 5 + +[nginx-botsearch] +enabled = true +port = http,https +logpath = %(nginx_error_log)s +bantime = 1w +maxretry = 5 diff --git a/ansible/roles/common/files/fail2ban/jails/sshd.local b/ansible/roles/common/files/fail2ban/jails/sshd.local new file mode 100644 index 0000000..1965b7d --- /dev/null +++ b/ansible/roles/common/files/fail2ban/jails/sshd.local @@ -0,0 +1,10 @@ +[sshd] +enabled = true +filter = sshd +banaction = iptables +backend = systemd +maxretry = 5 +findtime = 1d +bantime = 2w +ignoreip = 127.0.0.1/8 192.168.1.0/24 +logpath = %(sshd_log)s diff --git a/ansible/roles/common/handlers/main.yml b/ansible/roles/common/handlers/main.yml new file mode 100644 index 0000000..fbb5630 --- /dev/null +++ b/ansible/roles/common/handlers/main.yml @@ -0,0 +1,12 @@ +--- +- name: restart_sshd + become: true + service: + name: sshd + state: restarted + +- name: restart_fail2ban + become: true + service: + name: fail2ban + state: restarted diff --git a/ansible/roles/common/tasks/deps.yml b/ansible/roles/common/tasks/deps.yml new file mode 100644 index 0000000..db42852 --- /dev/null +++ b/ansible/roles/common/tasks/deps.yml @@ -0,0 +1,7 @@ +--- +- name: install common dependencies + become: true + pacman: + name: "{{ deps }}" + state: present + tags: deps diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml new file mode 100644 index 0000000..05e767d --- /dev/null +++ b/ansible/roles/common/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- import_tasks: deps.yml +- import_tasks: security.yml diff --git a/ansible/roles/common/tasks/security.yml b/ansible/roles/common/tasks/security.yml new file mode 100644 index 0000000..33885de --- /dev/null +++ b/ansible/roles/common/tasks/security.yml @@ -0,0 +1,31 @@ +--- +- name: ensure sshd disallows passwords + become: true + lineinfile: + path: /etc/ssh/sshd_config + regexp: "{{ item.re }}" + line: "{{ item.li }}" + with_items: + - {re: '^[# ]*PasswordAuthentication ', li: 'PasswordAuthentication no'} + - {re: '^[# ]*PermitEmptyPasswords ', li: 'PermitEmptyPasswords no'} + - {re: '^[# ]*PermitRootLogin ', li: 'PermitRootLogin no'} + notify: restart_sshd + tags: security + +- name: setup fail2ban jails + become: true + copy: + src: files/fail2ban/jails/{{ item }} + dest: /etc/fail2ban/jail.d/{{ item }} + with_items: "{{ fail2ban_jails }}" + notify: restart_fail2ban + tags: security + +- name: adjust fail2ban sshd filter + become: true + lineinfile: + path: /etc/fail2ban/filter.d/sshd.conf + regexp: '^[#]*filter =' + line: 'filter = sshd[mode=extra]' + notify: restart_fail2ban + tags: security diff --git a/ansible/roles/drone/meta/main.yml b/ansible/roles/drone/meta/main.yml new file mode 100644 index 0000000..3f81c4b --- /dev/null +++ b/ansible/roles/drone/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - role: http diff --git a/ansible/roles/drone/tasks/docker.yml b/ansible/roles/drone/tasks/docker.yml new file mode 100644 index 0000000..bd2ceef --- /dev/null +++ b/ansible/roles/drone/tasks/docker.yml @@ -0,0 +1,17 @@ +--- +- name: Create Drone CI container + community.general.docker_container: + name: drone + image: drone/drone + restart: true + restart_policy: on-failure + restart_retries: 3 + env: + DRONE_GITHUB_CLIENT_ID: {{ drone_gh_client_id }} + DRONE_GITHUB_CLIENT_SECRET: {{ drone_gh_client_sec }} + DRONE_RPC_SECRET: {{ drone_rpc_secret }} + DRONE_SERVER_HOST: {{ ci_server_name }} + DRONE_SERVER_PROTO: {{ drone_server_proto }} + DRONE_GIT_ALWAYS_AUTH: 'true' + DRONE_USER_FILTER: {{ drone_user_filter }} + diff --git a/ansible/roles/drone/tasks/main.yml b/ansible/roles/drone/tasks/main.yml new file mode 100644 index 0000000..9eab6da --- /dev/null +++ b/ansible/roles/drone/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- import_tasks: docker.yml diff --git a/ansible/roles/http/defaults/main.yml b/ansible/roles/http/defaults/main.yml new file mode 100644 index 0000000..479c3fc --- /dev/null +++ b/ansible/roles/http/defaults/main.yml @@ -0,0 +1,9 @@ +--- +ci_server_email: bastian@bdebyl.net +ci_server_name: ci.bdebyl.net + +deps: [ + certbot, + certbot-nginx, + nginx +] diff --git a/ansible/roles/http/files/nginx/nginx.conf b/ansible/roles/http/files/nginx/nginx.conf new file mode 100644 index 0000000..7dd225e --- /dev/null +++ b/ansible/roles/http/files/nginx/nginx.conf @@ -0,0 +1,36 @@ +user http; +worker_processes 1; + +error_log /var/log/nginx/error.log; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + gzip on; + gzip_disable "mise6"; + + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com; img-src 'self' data: https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-src 'none'; object-src 'none'"; + + limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s; + + include etc/nginx/sites-enabled/*.conf; +} diff --git a/ansible/roles/http/handlers/main.yml b/ansible/roles/http/handlers/main.yml new file mode 100644 index 0000000..4bfde1f --- /dev/null +++ b/ansible/roles/http/handlers/main.yml @@ -0,0 +1,6 @@ +--- +- name: restart_nginx + become: true + service: + name: nginx + state: restarted diff --git a/ansible/roles/http/meta/main.yml b/ansible/roles/http/meta/main.yml new file mode 100644 index 0000000..fdda41b --- /dev/null +++ b/ansible/roles/http/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - role: common diff --git a/ansible/roles/http/tasks/cron.yml b/ansible/roles/http/tasks/cron.yml new file mode 100644 index 0000000..236d983 --- /dev/null +++ b/ansible/roles/http/tasks/cron.yml @@ -0,0 +1,10 @@ +--- +- cron: renew certbot ssl certificate weekly + cron: + name: ci_bdebyl_net_renewal + special_time: weekly + job: | + certbot --renew certonly --webroot --webroot-path=/srv/http \ + -m {{ ci_server_email }} --agree-tos \ + -d {{ ci_server_name }} + tags: never diff --git a/ansible/roles/http/tasks/deps.yml b/ansible/roles/http/tasks/deps.yml new file mode 100644 index 0000000..aa8ae7d --- /dev/null +++ b/ansible/roles/http/tasks/deps.yml @@ -0,0 +1,7 @@ +--- +- name: install http dependencies + become: true + pacman: + name: "{{ deps }}" + state: present + tags: deps diff --git a/ansible/roles/http/tasks/http.yml b/ansible/roles/http/tasks/http.yml new file mode 100644 index 0000000..30e42f1 --- /dev/null +++ b/ansible/roles/http/tasks/http.yml @@ -0,0 +1,56 @@ +--- +- name: setup nginx base configuration + become: true + copy: + src: files/nginx/nginx.conf + dest: /etc/nginx/nginx.conf + notify: restart_nginx + tags: http + +- name: setup nginx directories + become: true + file: + path: "/etc/nginx/{{ item }}" + state: directory + with_items: + - sites-enabled + - sites-available + tags: http + +- name: chown http user home + become: true + file: + path: /srv/http + owner: http + group: http + recurse: true + tags: http + +- name: touch nginx logs, enable jail + become: true + file: + path: "/var/log/nginx/error.log" + state: file + notify: restart_fail2ban + tags: http, security + +- name: template nginx http sites-available + become: true + template: + src: "templates/nginx/sites/{{ item }}.j2" + dest: "/etc/nginx/sites-available/{{ item }}" + with_items: + - "{{ ci_server_name }}.http.conf" + notify: restart_nginx + tags: http + +- name: enable desired nginx http sites + become: true + file: + src: "/etc/nginx/sites-available/{{ item }}" + dest: "/etc/nginx/sites-enabled/{{ item }}" + state: link + with_items: + - "{{ ci_server_name }}.http.conf" + notify: restart_nginx + tags: http diff --git a/ansible/roles/http/tasks/main.yml b/ansible/roles/http/tasks/main.yml new file mode 100644 index 0000000..e49aa72 --- /dev/null +++ b/ansible/roles/http/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- import_tasks: deps.yml +- import_tasks: http.yml +- import_tasks: ssl.yml diff --git a/ansible/roles/http/tasks/ssl.yml b/ansible/roles/http/tasks/ssl.yml new file mode 100644 index 0000000..02ed23f --- /dev/null +++ b/ansible/roles/http/tasks/ssl.yml @@ -0,0 +1,59 @@ +--- +- name: flush existing nginx https enabled sites + become: true + file: + path: "/etc/nginx/sites-enabled/{{ item }}" + state: absent + with_items: + - "{{ ci_server_name }}.https.conf" + notify: restart_nginx + tags: ssl + +- meta: flush_handlers + tags: ssl + +- name: generate openssl dhparam for nginx + become: true + command: | + openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 2048 + args: + creates: /etc/ssl/certs/dhparam.pem + tags: ssl + +- name: create ssl certificate for ci server + become: true + command: | + certbot certonly --webroot --webroot-path=/srv/http \ + -m {{ ci_server_email }} --agree-tos \ + -d {{ ci_server_name }} + args: + creates: "/etc/letsencrypt/live/{{ ci_server_name }}" + tags: ssl + +- name: check if certbot certificate was created + become: true + stat: + path: "/etc/letsencrypt/live/{{ ci_server_name }}" + register: stat_result + tags: ssl + +- name: template nginx https sites-available + become: true + template: + src: "templates/nginx/sites/{{ item }}.j2" + dest: "/etc/nginx/sites-available/{{ item }}" + with_items: + - "{{ ci_server_name }}.https.conf" + tags: ssl + +- name: enable desired nginx https sites + become: true + file: + src: "/etc/nginx/sites-available/{{ item }}" + dest: "/etc/nginx/sites-enabled/{{ item }}" + state: link + with_items: + - "{{ ci_server_name }}.https.conf" + notify: restart_nginx + when: stat_result.stat.exists + tags: ssl diff --git a/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.http.conf.j2 b/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.http.conf.j2 new file mode 100644 index 0000000..48cc792 --- /dev/null +++ b/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.http.conf.j2 @@ -0,0 +1,14 @@ +server { + listen 80; + listen [::]:80; + server_name {{ ci_server_name }}; + + location /.well-known { + root /srv/http; + try_files $uri $uri/ =404; + } + + location / { + return 301 https://$host$request_uri; + } +} diff --git a/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.https.conf.j2 b/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.https.conf.j2 new file mode 100644 index 0000000..2b7c17b --- /dev/null +++ b/ansible/roles/http/templates/nginx/sites/ci.bdebyl.net.https.conf.j2 @@ -0,0 +1,35 @@ +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name {{ ci_server_name }}; + + add_header Strict-Transport-Security max-age=6307200; + + ssl_certificate /etc/letsencrypt/live/{{ ci_server_name }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ ci_server_name }}/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/{{ ci_server_name }}/fullchain.pem; + ssl_dhparam /etc/ssl/certs/dhparam.pem; + + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + ssl_stapling on; + ssl_stapling_verify on; + + ssl_protocols TLSv1.2 TLSv1.3; + 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_prefer_server_ciphers off; + + location / { + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_post; + + proxy_pass http://127.0.0.1:4242; + proxy_redirect off; + proxy_http_version 1.1; + proxy_buffering off; + + chunked_transfer_encoding off; + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6ccad51 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ansible-lint==4.3.5 +ansible==2.9.13 +yamllint==1.24.2