aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjaseg <code@jaseg.net>2020-01-17 11:11:52 +0100
committerjaseg <code@jaseg.net>2020-01-22 15:57:23 +0100
commita747ed293ec4449ca15271f9476903905dbbfaf5 (patch)
treefdbd089df1d9052ba7c886cbc3e235ea8c1faab9
parent6408f5a15c8dd3967b073e8a2d160b5f490f534a (diff)
downloadinfra-a747ed293ec4449ca15271f9476903905dbbfaf5.tar.gz
infra-a747ed293ec4449ca15271f9476903905dbbfaf5.tar.bz2
infra-a747ed293ec4449ca15271f9476903905dbbfaf5.zip
deploy: add notification proxy
-rw-r--r--.gitignore1
-rw-r--r--nginx.conf38
-rw-r--r--notification_proxy.py89
-rw-r--r--notification_proxy_config.py.j25
-rw-r--r--playbook.yml3
-rw-r--r--setup_notification_proxy.yml48
-rw-r--r--setup_webserver.yml2
-rw-r--r--uwsgi-notification-proxy.ini10
8 files changed, 196 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index c3129a6..e681fba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
*_secret.txt
+*_apikey.txt
playbook.retry
diff --git a/nginx.conf b/nginx.conf
index 8d21357..fb88cbd 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -132,6 +132,44 @@ http {
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
+ server_name automation.jaseg.de;
+ root /usr/share/nginx/html;
+
+ ssl_certificate "/etc/letsencrypt/live/automation.jaseg.de/fullchain.pem";
+ ssl_certificate_key "/etc/letsencrypt/live/automation.jaseg.de/privkey.pem";
+ ssl_dhparam "/etc/letsencrypt/ssl-dhparams.pem";
+ include /etc/letsencrypt/options-ssl-nginx.conf;
+
+ ssl_stapling on;
+ ssl_stapling_verify on;
+
+ resolver 67.207.67.2 67.207.67.3 valid=300s;
+ resolver_timeout 10s;
+
+ add_header Strict-Transport-Security "max-age=86400";
+
+ # Load configuration files for the default server block.
+ include /etc/nginx/default.d/*.conf;
+
+ location / {
+ include uwsgi_params;
+ uwsgi_pass unix:/run/uwsgi/notification-proxy.socket;
+ }
+
+ error_page 404 /404.html;
+ location = /40x.html {
+ root /usr/share/nginx/html;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+ }
+
+ server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
server_name kochbuch.jaseg.net kochbuch.jaseg.net;
root /usr/share/nginx/html;
diff --git a/notification_proxy.py b/notification_proxy.py
new file mode 100644
index 0000000..ae4d73e
--- /dev/null
+++ b/notification_proxy.py
@@ -0,0 +1,89 @@
+import smtplib
+import ssl
+import email.utils
+import hmac
+from email.mime.text import MIMEText
+from datetime import datetime
+import functools
+import json
+import binascii
+
+from flask import Flask, request, abort
+
+app = Flask(__name__)
+app.config.from_pyfile('config.py')
+
+smtp_server = "smtp.sendgrid.net"
+port = 465
+
+mail_routes = {}
+def mail_route(name, receiver, subject):
+ def wrap(func):
+ global routes
+ mail_routes[name] = (receiver, subject, func)
+ return func
+ return wrap
+
+
+def authenticate(secret):
+ def wrap(func):
+ func.last_seqnum = 0
+ @functools.wraps(func)
+ def wrapper(*args, **kwargs):
+ if not request.is_json:
+ abort(400)
+
+ if not 'auth' in request.json and 'payload' in request.json:
+ abort(400)
+
+ if not isinstance(request.json['auth'], str):
+ abort(400)
+ their_digest = binascii.unhexlify(request.json['auth'])
+
+ our_digest = hmac.digest(secret.encode('utf-8'), request.json['payload'].encode('utf-8'), 'sha256')
+ if not hmac.compare_digest(their_digest, our_digest):
+ abort(403)
+
+ try:
+ payload = json.loads(request.json['payload'])
+ except:
+ abort(400)
+
+ if not isinstance(payload['seq'], int) or payload['seq'] <= func.last_seqnum:
+ abort(400)
+
+ func.last_seqnum = payload['seq']
+ del payload['seq']
+ return func(payload)
+ return wrapper
+ return wrap
+
+@mail_route('klingel', 'computerstuff@jaseg.de', 'It rang!')
+@authenticate(app.config['SECRET_KLINGEL'])
+def klingel(_):
+ return f'Date: {datetime.utcnow().isoformat()}'
+
+
+@app.route('/notify/<route_name>', methods=['POST'])
+def notify(route_name):
+ try:
+ context = ssl.create_default_context()
+ smtp = smtplib.SMTP_SSL(smtp_server, port)
+ smtp.login('apikey', app.config['SENDGRID_APIKEY'])
+
+ sender = f'{route_name}@{app.config["DOMAIN"]}'
+
+ receiver, subject, func = mail_routes[route_name]
+
+ msg = MIMEText(func() or subject)
+ msg['Subject'] = subject
+ msg['From'] = sender
+ msg['To'] = receiver
+ msg['Date'] = email.utils.formatdate()
+ smtp.sendmail(sender, receiver, msg.as_string())
+ finally:
+ smtp.quit()
+ return 'success'
+
+if __name__ == '__main__':
+ app.run()
diff --git a/notification_proxy_config.py.j2 b/notification_proxy_config.py.j2
new file mode 100644
index 0000000..ea53e34
--- /dev/null
+++ b/notification_proxy_config.py.j2
@@ -0,0 +1,5 @@
+
+SENDGRID_APIKEY = '{{lookup('file', 'notification_proxy_sendgrid_apikey.txt')}}'
+DOMAIN = 'automation.jaseg.de'
+
+SECRET_KLINGEL = '{{lookup('password', 'notification_proxy_klingel_secret.txt length=32')}}'
diff --git a/playbook.yml b/playbook.yml
index 0d16d7f..2db45bc 100644
--- a/playbook.yml
+++ b/playbook.yml
@@ -71,3 +71,6 @@
- name: Setup pogojig
include_tasks: setup_pogojig.yml
+ - name: Setup notification proxy
+ include_tasks: setup_notification_proxy.yml
+
diff --git a/setup_notification_proxy.yml b/setup_notification_proxy.yml
new file mode 100644
index 0000000..3f86412
--- /dev/null
+++ b/setup_notification_proxy.yml
@@ -0,0 +1,48 @@
+---
+- name: Create notification proxy worker user and group
+ user:
+ name: uwsgi-notification-proxy
+ create_home: no
+ group: uwsgi
+ password: '!'
+ shell: /sbin/nologin
+ system: yes
+
+- name: Create webapp dir
+ file:
+ path: /var/lib/notification-proxy
+ state: directory
+ owner: uwsgi-notification-proxy
+ group: uwsgi
+ mode: 0550
+
+- name: Copy webapp sources
+ copy:
+ src: notification_proxy.py
+ dest: /var/lib/notification-proxy/
+ owner: uwsgi-notification-proxy
+ group: uwsgi
+ mode: 0440
+
+- name: Template webapp config
+ template:
+ src: notification_proxy_config.py.j2
+ dest: /var/lib/notification-proxy/config.py
+ owner: uwsgi-notification-proxy
+ group: root
+ mode: 0660
+
+- name: Copy uwsgi config
+ copy:
+ src: uwsgi-notification-proxy.ini
+ dest: /etc/uwsgi.d/notification-proxy.ini
+ owner: uwsgi-notification-proxy
+ group: uwsgi
+ mode: 0440
+
+- name: Enable uwsgi systemd socket
+ systemd:
+ daemon-reload: yes
+ name: uwsgi-app@notification-proxy.socket
+ enabled: yes
+
diff --git a/setup_webserver.yml b/setup_webserver.yml
index 1405bed..8f1f429 100644
--- a/setup_webserver.yml
+++ b/setup_webserver.yml
@@ -21,6 +21,7 @@
- kochbuch.jaseg.net
- tracespace.jaseg.net
- openjscad.jaseg.net
+ - automation.jaseg.de
- name: Copy uwsgi systemd socket config
copy:
@@ -54,6 +55,7 @@
- tracespace.jaseg.net
- openjscad.jaseg.net
- pogojig.jaseg.net
+ - automation.jaseg.de
- name: Copy final nginx config
copy:
diff --git a/uwsgi-notification-proxy.ini b/uwsgi-notification-proxy.ini
new file mode 100644
index 0000000..aab2b5a
--- /dev/null
+++ b/uwsgi-notification-proxy.ini
@@ -0,0 +1,10 @@
+[uwsgi]
+master = True
+cheap = True
+die-on-idle = False
+manage-script-name = True
+log-format = [pid: %(pid)|app: -|req: -/-] %(addr) (%(user)) {%(vars) vars in %(pktsize) bytes} [%(ctime)] %(method) [URI hidden] => generated %(rsize) bytes in %(msecs) msecs (%(proto) %(status)) %(headers) headers in %(hsize) bytes (%(switches) switches on core %(core))
+plugins = python3
+chdir = /var/lib/notification-proxy
+mount = /=notification_proxy:app
+