Moved the web app to a separate directory.
This commit is contained in:
22
app/Dockerfile
Normal file
22
app/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache python3 py3-pip
|
||||
|
||||
# Copy requirements and install Python packages
|
||||
COPY requirements.txt /tmp/requirements.txt
|
||||
RUN pip3 install --break-system-packages -r /tmp/requirements.txt
|
||||
|
||||
# Clean up apk cache
|
||||
RUN rm -rf /var/cache/apk/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY app.py /app/app.py
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
ENV LISTEN_ADDRESS=0.0.0.0
|
||||
ENV LISTEN_PORT=8080
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
101
app/app.py
Executable file
101
app/app.py
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from flask import Flask, Response, request, jsonify
|
||||
from flask_httpauth import HTTPBasicAuth
|
||||
from base64 import b64encode
|
||||
from requests import get, put, delete
|
||||
from os import getenv
|
||||
import jmespath
|
||||
|
||||
# Define global variables for MIAB host and auth header, to be set during authentication
|
||||
MIAB_HOST = getenv('MIAB_HOST', 'localhost')
|
||||
MIAB_AUTH_HEADER = ''
|
||||
|
||||
app = Flask('dns-updater')
|
||||
auth = HTTPBasicAuth()
|
||||
|
||||
# Verify username and password by attempting to access a dummy DNS record
|
||||
@auth.verify_password
|
||||
def verify(username, password):
|
||||
global MIAB_AUTH_HEADER, MIAB_HOST
|
||||
MIAB_AUTH_HEADER = "Basic " + b64encode(f"{username}:{password}".encode()).decode()
|
||||
result = get(f'https://{MIAB_HOST}/admin/dns/custom/test/A', headers={'Authorization': MIAB_AUTH_HEADER})
|
||||
if result.status_code != 200:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@app.route('/api/setrecord/<qname>/<value>', methods=['GET'], defaults={'rtype': 'A'})
|
||||
@app.route('/api/setrecord/<qname>/<rtype>/<value>', methods=['GET'])
|
||||
@app.route('/api/setrecord/<qname>', methods=['PUT'], defaults={'rtype': 'A'})
|
||||
@app.route('/api/setrecord/<qname>/<rtype>', methods=['PUT'])
|
||||
@auth.login_required
|
||||
def set_record(qname, rtype, value = None):
|
||||
global MIAB_HOST, MIAB_AUTH_HEADER
|
||||
|
||||
# Use request data as value for PUT method
|
||||
if request.method == 'PUT':
|
||||
value = request.data.decode()
|
||||
|
||||
url = f'https://{MIAB_HOST}/admin/dns/custom/{qname}/{rtype}'
|
||||
|
||||
# Check the currect value
|
||||
resp = get(url, headers={'Authorization': MIAB_AUTH_HEADER})
|
||||
|
||||
if resp.status_code == 200:
|
||||
current_value = jmespath.compile('[0].value').search(resp.json())
|
||||
if current_value == value:
|
||||
return f'OK, no change' # No change needed
|
||||
|
||||
# Values are different or record does not exist, proceed to set the new value
|
||||
resp = put(url, headers={'Authorization': MIAB_AUTH_HEADER, 'Content-Type': 'text/plain'}, data=value)
|
||||
|
||||
# Propagate 400-499 as is, 500+ as 502
|
||||
if resp.status_code != 200:
|
||||
if 400 <= resp.status_code < 500:
|
||||
status = resp.status_code
|
||||
elif resp.status_code >= 500:
|
||||
status = 502
|
||||
else:
|
||||
status = 500
|
||||
return Response(f'ERROR: {resp.status_code}\n', status=status)
|
||||
|
||||
# Success
|
||||
return f'OK, record set'
|
||||
|
||||
@app.route('/api/deleterecord/<qname>/<rtype>', methods=['GET', 'DELETE'])
|
||||
@auth.login_required
|
||||
def delete_record(qname, rtype):
|
||||
global MIAB_HOST, MIAB_AUTH_HEADER
|
||||
url = f'https://{MIAB_HOST}/admin/dns/custom/{qname}/{rtype}'
|
||||
resp = delete(url, headers={'Authorization': MIAB_AUTH_HEADER, 'Content-Type': 'text/plain'}, data='')
|
||||
|
||||
# Propagate 400-499 as is, 500+ as 502
|
||||
if resp.status_code != 200:
|
||||
if 400 <= resp.status_code < 500:
|
||||
status = resp.status_code
|
||||
elif resp.status_code >= 500:
|
||||
status = 502
|
||||
else:
|
||||
status = 500
|
||||
return Response(f'ERROR\n', status=status)
|
||||
|
||||
# Success
|
||||
return f'OK, record deleted.\n'
|
||||
|
||||
@app.route('/api/listrecords', methods=['GET'], defaults={'qname': None, 'rtype': None})
|
||||
@app.route('/api/listrecords/<qname>', methods=['GET'], defaults={'rtype': None})
|
||||
@app.route('/api/listrecords/<qname>/<rtype>', methods=['GET'])
|
||||
@auth.login_required
|
||||
def list_records(qname, rtype):
|
||||
global MIAB_HOST, MIAB_AUTH_HEADER
|
||||
url = f'https://{MIAB_HOST}/admin/dns/custom'
|
||||
if qname:
|
||||
url += f'/{qname}'
|
||||
if rtype:
|
||||
url += f'/{rtype}'
|
||||
resp = get(url, headers={'Authorization': MIAB_AUTH_HEADER})
|
||||
return jsonify(resp.json())
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=8080)
|
||||
14
app/dns-updater.service
Normal file
14
app/dns-updater.service
Normal file
@@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=dns-updater
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory=/opt/dns-updater
|
||||
Environment="MIAB_HOST=box.koszewscy.waw.pl"
|
||||
ExecStart=/usr/bin/gunicorn --workers 4 --bind 0.0.0.0:8443 --certfile="/miab-data/ssl/ssl_certificate.pem" --keyfile="/miab-data/ssl/ssl_private_key.pem" app:app
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
14
app/entrypoint.sh
Normal file
14
app/entrypoint.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
LISTEN_ADDRESS="${LISTEN_ADDRESS:-0.0.0.0}"
|
||||
LISTEN_PORT="${LISTEN_PORT:-8080}"
|
||||
|
||||
cd /app
|
||||
|
||||
if [ -z "$CERT_FILE" ] || [ -z "$KEY_FILE" ]; then
|
||||
exec gunicorn --bind ${LISTEN_ADDRESS}:${LISTEN_PORT} app:app
|
||||
else
|
||||
exec gunicorn --bind ${LISTEN_ADDRESS}:${LISTEN_PORT} --certfile=${CERT_FILE} --keyfile=${KEY_FILE} app:app
|
||||
fi
|
||||
13
app/install
Executable file
13
app/install
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
if [[ $(id -u) -ne 0 ]]; then
|
||||
echo "This script must be run as root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p /opt/dns-updater
|
||||
cp app.py /opt/dns-updater/
|
||||
cp dns-updater.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now dns-updater.service
|
||||
systemctl status dns-updater.service
|
||||
5
app/requirements.txt
Normal file
5
app/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
flask
|
||||
flask-httpauth
|
||||
gunicorn
|
||||
requests
|
||||
jmespath
|
||||
Reference in New Issue
Block a user