Move application into package and use factory
This commit is contained in:
parent
95b0a73e78
commit
438f8eaf63
137
app.py
137
app.py
@ -1,137 +0,0 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_httpauth import HTTPBasicAuth
|
||||
from werkzeug.security import check_password_hash
|
||||
from ipaddress import ip_address
|
||||
import re
|
||||
from pfctl import pfctl_table_op, pfctl_cfg_read, pfctl_cfg_write
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
auth = HTTPBasicAuth()
|
||||
|
||||
users = {
|
||||
"erg.verweg.com": 'pbkdf2:sha256:260000$leXVKkMYNu60eQZR$0893397beb241931d33d2c996e66447a375d3b7923aa32fc4af6b80eec716fbe'
|
||||
}
|
||||
|
||||
PAT_PORT = r'^any(?:\s+port\s+{\w+(?:,\w+)*})?$'
|
||||
PAT_PROT = r'^(?:tcp|udp)$'
|
||||
PAT_NAME = r'^[\w\-]+$'
|
||||
|
||||
|
||||
def untaint(pattern, string):
|
||||
'''
|
||||
untaint string (as perl does)
|
||||
'''
|
||||
match = re.match(pattern, string)
|
||||
if match:
|
||||
return match.string
|
||||
else:
|
||||
raise ValueError(f'"{string}" is tainted')
|
||||
|
||||
|
||||
@auth.verify_password
|
||||
def verify_password(username, password):
|
||||
if username in users and \
|
||||
check_password_hash(users.get(username), password):
|
||||
return username
|
||||
|
||||
|
||||
@app.route("/flush/<name>", methods=['GET'])
|
||||
@auth.login_required
|
||||
def flush(name):
|
||||
remote_user = auth.username()
|
||||
name = untaint(PAT_NAME, name)
|
||||
app.logger.info(f'Flushing table f2b-{name}'
|
||||
' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op('f2b-jail/{remote_user}',
|
||||
table='f2b-{name}',
|
||||
operation='flush')
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'operation': 'flush',
|
||||
'result': res})
|
||||
|
||||
|
||||
@app.route("/register", methods=['PUT', 'DELETE'])
|
||||
@auth.login_required
|
||||
def register():
|
||||
remote_user = auth.username()
|
||||
# port / name / protocol
|
||||
data = request.get_json()
|
||||
name = untaint(PAT_NAME, data['name'])
|
||||
protocol = untaint(PAT_PROT, data['protocol'])
|
||||
port = untaint(PAT_PORT, data['port'])
|
||||
cfg = pfctl_cfg_read(f'f2b-jail/{remote_user}')
|
||||
if not cfg:
|
||||
cfg = []
|
||||
if request.method == 'PUT':
|
||||
cfg.extend([
|
||||
bytes(f'table <f2b-{name}> persist counters', 'ascii'),
|
||||
bytes(f'block quick proto {protocol}'
|
||||
f' from <f2b-{name}> to {port}', 'ascii')
|
||||
])
|
||||
res = pfctl_cfg_write(f'f2b-jail/{remote_user}',
|
||||
b'\n'.join(cfg) + b'\n')
|
||||
elif request.method == 'DELETE':
|
||||
cfg = [cfg_line for cfg_line in cfg
|
||||
if cfg_line.find(bytes(f'<f2b-{name}>', 'ascii')) == -1]
|
||||
res = pfctl_cfg_write(f'f2b-jail/{remote_user}',
|
||||
b'\n'.join(cfg) + b'\n')
|
||||
pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='flush')
|
||||
pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='kill')
|
||||
app.logger.info(f'pfctl -a f2b-jail/{remote_user} -f-')
|
||||
return jsonify({'remote_user': remote_user, 'data': data})
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'action': 'start' if request.method == 'PUT'
|
||||
else 'stop',
|
||||
'result': res})
|
||||
|
||||
|
||||
@app.route("/ban", methods=['PUT', 'DELETE'])
|
||||
@auth.login_required
|
||||
def ban():
|
||||
remote_user = auth.username()
|
||||
data = request.get_json()
|
||||
# name / ip
|
||||
name = untaint(PAT_NAME, data['name'])
|
||||
ip = ip_address(data['ip'])
|
||||
if request.method == 'PUT':
|
||||
app.logger.info(f'Add {ip} to f2b-{name}'
|
||||
f' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='add',
|
||||
value=str(ip))
|
||||
elif request.method == 'DELETE':
|
||||
app.logger.info(f'Remove {ip} from f2b-{name}'
|
||||
f' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='delete',
|
||||
value=str(ip))
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'operation': 'add' if request.method == 'PUT'
|
||||
else 'delete',
|
||||
'result': res})
|
||||
|
||||
|
||||
@app.errorhandler(ValueError)
|
||||
def permission_err(error):
|
||||
'''
|
||||
Show a json parsable error if the value is illegal
|
||||
'''
|
||||
app.logger.fatal(error)
|
||||
return jsonify({'error': str(error)}), 500
|
||||
|
||||
|
||||
@auth.error_handler
|
||||
def auth_error():
|
||||
app.logger.error('Access Denied')
|
||||
return jsonify({'error': 'Access Denied'}), 401
|
135
jail2ban/__init__.py
Normal file
135
jail2ban/__init__.py
Normal file
@ -0,0 +1,135 @@
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_httpauth import HTTPBasicAuth
|
||||
from werkzeug.security import check_password_hash
|
||||
from ipaddress import ip_address
|
||||
import re
|
||||
from pfctl import pfctl_table_op, pfctl_cfg_read, pfctl_cfg_write
|
||||
|
||||
|
||||
auth = HTTPBasicAuth()
|
||||
|
||||
users = {
|
||||
"erg.verweg.com": 'pbkdf2:sha256:260000$leXVKkMYNu60eQZR$0893397beb241931d33d2c996e66447a375d3b7923aa32fc4af6b80eec716fbe'
|
||||
}
|
||||
|
||||
PAT_PORT = r'^any(?:\s+port\s+{\w+(?:,\w+)*})?$'
|
||||
PAT_PROT = r'^(?:tcp|udp)$'
|
||||
PAT_NAME = r'^[\w\-]+$'
|
||||
|
||||
|
||||
def untaint(pattern, string):
|
||||
'''
|
||||
untaint string (as perl does)
|
||||
'''
|
||||
match = re.match(pattern, string)
|
||||
if match:
|
||||
return match.string
|
||||
else:
|
||||
raise ValueError(f'"{string}" is tainted')
|
||||
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__, instance_relative_config=True)
|
||||
|
||||
@auth.verify_password
|
||||
def verify_password(username, password):
|
||||
if username in users and \
|
||||
check_password_hash(users.get(username), password):
|
||||
return username
|
||||
|
||||
@app.route("/flush/<name>", methods=['GET'])
|
||||
@auth.login_required
|
||||
def flush(name):
|
||||
remote_user = auth.username()
|
||||
name = untaint(PAT_NAME, name)
|
||||
app.logger.info(f'Flushing table f2b-{name}'
|
||||
f' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op('f2b-jail/{remote_user}',
|
||||
table='f2b-{name}',
|
||||
operation='flush')
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'operation': 'flush',
|
||||
'result': res})
|
||||
|
||||
@app.route("/register", methods=['PUT', 'DELETE'])
|
||||
@auth.login_required
|
||||
def register():
|
||||
remote_user = auth.username()
|
||||
# port / name / protocol
|
||||
data = request.get_json()
|
||||
name = untaint(PAT_NAME, data['name'])
|
||||
protocol = untaint(PAT_PROT, data['protocol'])
|
||||
port = untaint(PAT_PORT, data['port'])
|
||||
cfg = pfctl_cfg_read(f'f2b-jail/{remote_user}')
|
||||
if not cfg:
|
||||
cfg = []
|
||||
if request.method == 'PUT':
|
||||
cfg.extend([
|
||||
bytes(f'table <f2b-{name}> persist counters', 'ascii'),
|
||||
bytes(f'block quick proto {protocol}'
|
||||
f' from <f2b-{name}> to {port}', 'ascii')
|
||||
])
|
||||
res = pfctl_cfg_write(f'f2b-jail/{remote_user}',
|
||||
b'\n'.join(cfg) + b'\n')
|
||||
elif request.method == 'DELETE':
|
||||
cfg = [cfg_line for cfg_line in cfg
|
||||
if cfg_line.find(bytes(f'<f2b-{name}>', 'ascii')) == -1]
|
||||
res = pfctl_cfg_write(f'f2b-jail/{remote_user}',
|
||||
b'\n'.join(cfg) + b'\n')
|
||||
pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='flush')
|
||||
pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='kill')
|
||||
app.logger.info(f'pfctl -a f2b-jail/{remote_user} -f-')
|
||||
return jsonify({'remote_user': remote_user, 'data': data})
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'action': 'start' if request.method == 'PUT'
|
||||
else 'stop',
|
||||
'result': res})
|
||||
|
||||
@app.route("/ban", methods=['PUT', 'DELETE'])
|
||||
@auth.login_required
|
||||
def ban():
|
||||
remote_user = auth.username()
|
||||
data = request.get_json()
|
||||
# name / ip
|
||||
name = untaint(PAT_NAME, data['name'])
|
||||
ip = ip_address(data['ip'])
|
||||
if request.method == 'PUT':
|
||||
app.logger.info(f'Add {ip} to f2b-{name}'
|
||||
f' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='add',
|
||||
value=str(ip))
|
||||
elif request.method == 'DELETE':
|
||||
app.logger.info(f'Remove {ip} from f2b-{name}'
|
||||
f' in anchor f2b-jail/{remote_user}')
|
||||
res = pfctl_table_op(f'f2b-jail/{remote_user}',
|
||||
table=f'f2b-{name}',
|
||||
operation='delete',
|
||||
value=str(ip))
|
||||
return jsonify({'anchor': f'f2b-jail/{remote_user}',
|
||||
'table': f'f2b-{name}',
|
||||
'operation': 'add' if request.method == 'PUT'
|
||||
else 'delete',
|
||||
'result': res})
|
||||
|
||||
@app.errorhandler(ValueError)
|
||||
def permission_err(error):
|
||||
'''
|
||||
Show a json parsable error if the value is illegal
|
||||
'''
|
||||
app.logger.fatal(error)
|
||||
return jsonify({'error': str(error)}), 500
|
||||
|
||||
@auth.error_handler
|
||||
def auth_error():
|
||||
app.logger.error('Access Denied')
|
||||
return jsonify({'error': 'Access Denied'}), 401
|
||||
|
||||
return app
|
Loading…
x
Reference in New Issue
Block a user