Files
fail2ban-webui/app/webui/templates/index.html
T
schiwagoaandClaude Opus 5.5 2e3f79236c Initial version of fail2ban-webui
Web interface to view, ban and unban fail2ban IPs on several hosts via
SSH, generalised from the private f2b-web tool.

- Web UI (Flask/Waitress): hosts.yaml, local/OIDC/proxy authentication,
  English/German UI, origin map, optional Loki/Grafana/GeoIP, CLI helpers
- Agent f2b-webctl with install.sh/uninstall.sh (dedicated user + sudo,
  from= restriction, protected networks, manual jail)
- Hardened Docker image, examples, documentation, smoke test, testbed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 15:38:17 +01:00

80 lines
3.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block header_meta %}
<span id="generated">–</span>
<button id="refresh" type="button">{{ t('refresh') }}</button>
{% if client_config.grafana and client_config.grafanaDashboard %}<a class="btn-link" href="{{ client_config.grafana }}{{ client_config.grafanaDashboard }}" target="_blank" rel="noopener">{{ t('grafana') }}</a>{% endif %}
{% endblock %}
{% block content %}
<main>
<section id="hosts" class="cards"></section>
{% if geo_enabled %}
<section class="panel map-panel">
<div class="toolbar">
<h2>{{ t('map_heading') }}</h2>
<span id="map-summary" class="muted"></span>
<button id="map-reset" type="button" class="small" hidden>{{ t('map_reset') }}</button>
</div>
<div class="map">
<svg id="map" viewBox="0 5 360 145" role="img" aria-label="{{ t('map_heading') }}">
<image href="{{ url_for('static', filename='world.svg') }}" x="0" y="5" width="360" height="145"/>
<g id="map-points"></g>
</svg>
</div>
<p class="hint">{{ t('map_hint') }}</p>
</section>
{% endif %}
<section class="panel">
<h2>{{ t('ban_heading') }}</h2>
<form id="ban-form" autocomplete="off">
<input id="ban-ip" type="text" placeholder="{{ t('ban_ip_placeholder') }}" required>
<input id="ban-note" type="text" placeholder="{{ t('ban_note_placeholder') }}" maxlength="200">
<span class="hosts-select">
{% for h in hosts %}<label><input type="checkbox" name="ban-host" value="{{ h }}" checked> {{ h }}</label>{% endfor %}
</span>
<button type="submit" class="danger">{{ t('ban_button') }}</button>
</form>
<p class="hint">{{ t('ban_hint', jail=manual_jail) }}</p>
</section>
<section class="panel">
<div class="toolbar">
<h2>{{ t('bans_heading') }} <span id="count" class="badge">0</span></h2>
<input id="filter-text" type="search" placeholder="{{ t('filter_search') }}">
<select id="filter-host"><option value="">{{ t('filter_all_hosts') }}</option></select>
<select id="filter-jail"><option value="">{{ t('filter_all_jails') }}</option></select>
</div>
<div class="table-wrap">
<table>
<thead>
<tr><th>{{ t('col_host') }}</th><th>{{ t('col_jail') }}</th><th>{{ t('col_ip') }}</th><th>{{ t('col_origin') }}</th><th>{{ t('col_since') }}</th><th>{{ t('col_until') }}</th><th>{{ t('col_actions') }}</th></tr>
</thead>
<tbody id="bans"><tr><td colspan="7" class="muted">{{ t('loading') }}</td></tr></tbody>
</table>
</div>
</section>
<section class="panel">
<h2>{{ t('audit_heading') }}</h2>
<ul id="audit" class="audit"></ul>
</section>
</main>
<dialog id="history">
<div class="dialog-head">
<h2 id="history-title"></h2>
<button type="button" id="history-close" aria-label="{{ t('history_close') }}">✕</button>
</div>
<div id="history-body"></div>
</dialog>
<div id="toast" role="status" aria-live="polite"></div>
<script id="i18n" type="application/json">{{ i18n | tojson }}</script>
<script id="client-config" type="application/json">{{ client_config | tojson }}</script>
{% endblock %}
{% block scripts %}<script src="{{ url_for('static', filename='app.js') }}"></script>{% endblock %}