Skip to content

Instantly share code, notes, and snippets.

@sorz
Last active April 15, 2018 07:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sorz/dedb653c2f73a49bc445739a6c509963 to your computer and use it in GitHub Desktop.
Save sorz/dedb653c2f73a49bc445739a6c509963 to your computer and use it in GitHub Desktop.
[Unit]
Description=Comment sender for example.com
[Service]
Type=simple
User=nobody
Group=nobody
ExecStart=/usr/bin/python3 /opt/comment-sender/sender.py
PrivateDevices=True
ProtectSystem=strict
ProtectHome=True
NoNewPrivileges=True
[Install]
WantedBy=multi-user.target
http {
# ...
limit_req_zone $binary_remote_addr zone=per_min:1m rate=1r/m;
server {
# ....
location / {
try_files $uri $uri/ $uri.html =404;
}
location = /ping {
proxy_pass http://127.0.0.80:8000/ping;
}
location = /send {
proxy_pass http://127.0.80.10:8000/send;
limit_req zone=per_min burst=2 nodelay;
}
}
}
#!/usr/bin/env python3
import time
from datetime import date
from smtplib import SMTP
from email.message import EmailMessage
from flask import Flask, Response, request
from werkzeug.datastructures import Headers
app = Flask(__name__)
LIMIT_MIN_INTERVAL = 180
LIMIT_BURST = 5
limit_left = LIMIT_BURST
limit_last = time.time()
def has_token():
return limit_left > 0 or time.time() - limit_last > LIMIT_MIN_INTERVAL
def response_limited():
headers = Headers()
headers.add('retry-after', LIMIT_MIN_INTERVAL)
return Response(status=429, headers=headers)
@app.route("/ping", methods=['GET'])
def ping():
return '', 204 if has_token() else response_limited()
@app.route("/send", methods=['POST'])
def post():
global limit_left, limit_last
if not has_token():
return response_limited()
if limit_left < LIMIT_BURST:
new_tokens = (time.time() - limit_last) // LIMIT_MIN_INTERVAL
limit_left = min(LIMIT_BURST, limit_left + new_tokens)
limit_left -= 1
limit_last = time.time()
name = request.form.get('name') or 'anonymous'
email = request.form.get('email') or 'no-reply@sorz.org'
message = request.form['message']
send_message(name, email, message)
return 'sent', 201
def send_message(name, reply, content):
msg = EmailMessage()
msg.set_content(content)
msg['Subject'] = f'Message on example.com ({date.today()})'
msg['From'] = f'{name} <comments@example.com>'
msg['To'] = 'you@example.com'
msg['Reply-To'] = f"{name} <{reply}>"
with SMTP('localhost') as smtp:
smtp.send_message(msg)
if __name__ == "__main__":
app.run('127.0.0.80', 8000)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment