Adds a web interface for ratelimiter
parent
84354b183d
commit
9c129fcf76
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
|
||||
class RateLimiter {
|
||||
private $db;
|
||||
public $db;
|
||||
private $log;
|
||||
private $maxAttempts = 5; // Maximum login attempts
|
||||
private $decayMinutes = 15; // Time window in minutes
|
||||
private $autoBlacklistThreshold = 10; // Attempts before auto-blacklist
|
||||
private $autoBlacklistDuration = 24; // Hours to blacklist for
|
||||
private $ratelimitTable = 'login_attempts';
|
||||
private $whitelistTable = 'ip_whitelist';
|
||||
private $blacklistTable = 'ip_blacklist';
|
||||
public $maxAttempts = 5; // Maximum login attempts
|
||||
public $decayMinutes = 15; // Time window in minutes
|
||||
public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist
|
||||
public $autoBlacklistDuration = 24; // Hours to blacklist for
|
||||
public $ratelimitTable = 'login_attempts';
|
||||
public $whitelistTable = 'ip_whitelist';
|
||||
public $blacklistTable = 'ip_blacklist';
|
||||
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
// Check if user has any of the required rights
|
||||
if (!($userObject->hasRight($user_id, 'superuser') ||
|
||||
$userObject->hasRight($user_id, 'edit whitelist') ||
|
||||
$userObject->hasRight($user_id, 'edit blacklist') ||
|
||||
$userObject->hasRight($user_id, 'edit ratelimiting'))) {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$action = $_GET['action'] ?? 'view';
|
||||
$section = $_GET['section'] ?? 'whitelist';
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
switch ($_POST['action']) {
|
||||
case 'add_whitelist':
|
||||
if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) {
|
||||
$ip = $_POST['ip_address'];
|
||||
$description = $_POST['description'];
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
$rateLimiter->addToWhitelist($ip, $is_network, $description, $currentUser);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'remove_whitelist':
|
||||
if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) {
|
||||
$ip = $_POST['ip_address'];
|
||||
$rateLimiter->removeFromWhitelist($ip, $user_id, $currentUser);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'add_blacklist':
|
||||
if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist')) {
|
||||
$ip = $_POST['ip_address'];
|
||||
$reason = $_POST['reason'];
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
$expiry_hours = empty($_POST['expiry_hours']) ? null : intval($_POST['expiry_hours']);
|
||||
$rateLimiter->addToBlacklist($ip, $is_network, $reason, $currentUser, null, $expiry_hours);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'remove_blacklist':
|
||||
if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist')) {
|
||||
$ip = $_POST['ip_address'];
|
||||
$rateLimiter->removeFromBlacklist($ip, $user_id, $currentUser);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Redirect to prevent form resubmission
|
||||
header("Location: {$app_root}?page=security§ion={$section}");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get the lists
|
||||
$whitelisted = $rateLimiter->getWhitelistedIps();
|
||||
$blacklisted = $rateLimiter->getBlacklistedIps();
|
||||
|
||||
// Include the template
|
||||
include '../app/templates/security.php';
|
||||
?>
|
|
@ -92,12 +92,23 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
|||
<i class="fas fa-wrench" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>config file
|
||||
</li>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') ||
|
||||
$userObject->hasRight($user_id, 'edit whitelist') ||
|
||||
$userObject->hasRight($user_id, 'edit blacklist') ||
|
||||
$userObject->hasRight($user_id, 'edit ratelimiting')) { ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=security">
|
||||
<li class="list-group-item<?php if ($page === 'security') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-shield-alt" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="security"></i>security
|
||||
</li>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=status">
|
||||
<li class="list-group-item<?php if ($page === 'status' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-heartbeat" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="status"></i>status
|
||||
</li>
|
||||
</a>
|
||||
|
||||
<?php if ($userObject->hasRight($user_id, 'view app logs')) {?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=logs">
|
||||
<li class="list-group-item<?php if ($page === 'logs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
<!-- Security Settings -->
|
||||
<div class="container">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Security Settings</h2>
|
||||
<ul class="nav nav-tabs">
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $section === 'whitelist' ? 'active' : '' ?>" href="?page=security§ion=whitelist">IP Whitelist</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist')) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $section === 'blacklist' ? 'active' : '' ?>" href="?page=security§ion=blacklist">IP Blacklist</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting')) { ?>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= $section === 'ratelimit' ? 'active' : '' ?>" href="?page=security§ion=ratelimit">Rate Limiting</a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($section === 'whitelist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist'))) { ?>
|
||||
<!-- Whitelist Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>IP Whitelist</h3>
|
||||
IP addresses and networks that will always bypass the ratelimiting login checks.
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" class="mb-4">
|
||||
<input type="hidden" name="action" value="add_whitelist">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="ip_address" placeholder="IP Address or CIDR" required>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control" name="description" placeholder="Description">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_white">
|
||||
<label class="form-check-label" for="is_network_white">Is Network</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Add to Whitelist</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Network</th>
|
||||
<th>Description</th>
|
||||
<th>Added By</th>
|
||||
<th>Added On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($whitelisted as $ip) { ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
|
||||
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
|
||||
<td><?= htmlspecialchars($ip['description']) ?></td>
|
||||
<td><?= htmlspecialchars($ip['created_by']) ?></td>
|
||||
<td><?= htmlspecialchars($ip['created_at']) ?></td>
|
||||
<td>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="remove_whitelist">
|
||||
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from whitelist?')">Remove</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($section === 'blacklist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist'))) { ?>
|
||||
<!-- Blacklist Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>IP Blacklist</h3>
|
||||
IP addresses and networks that will always get blocked at login.
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" class="mb-4">
|
||||
<input type="hidden" name="action" value="add_blacklist">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" name="ip_address" placeholder="IP Address or CIDR" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="text" class="form-control" name="reason" placeholder="Reason">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="number" class="form-control" name="expiry_hours" placeholder="Expiry (hours)">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_black">
|
||||
<label class="form-check-label" for="is_network_black">Is Network</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Add to Blacklist</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Network</th>
|
||||
<th>Reason</th>
|
||||
<th>Added By</th>
|
||||
<th>Added On</th>
|
||||
<th>Expires</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($blacklisted as $ip) { ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
|
||||
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
|
||||
<td><?= htmlspecialchars($ip['reason']) ?></td>
|
||||
<td><?= htmlspecialchars($ip['created_by']) ?></td>
|
||||
<td><?= htmlspecialchars($ip['created_at']) ?></td>
|
||||
<td><?= $ip['expiry_time'] ? htmlspecialchars($ip['expiry_time']) : 'Never' ?></td>
|
||||
<td>
|
||||
<form method="POST" style="display: inline;">
|
||||
<input type="hidden" name="action" value="remove_blacklist">
|
||||
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from blacklist?')">Remove</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($section === 'ratelimit' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting'))) { ?>
|
||||
<!-- Rate Limiting Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Rate Limiting Settings</h3>
|
||||
Restricts brute force or flooding attempts at login page.
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h4>Current Settings</h4>
|
||||
<ul>
|
||||
<li>Maximum login attempts: <?= $rateLimiter->maxAttempts ?></li>
|
||||
<li>Time window: <?= $rateLimiter->decayMinutes ?> minutes</li>
|
||||
<li>Auto-blacklist threshold: <?= $rateLimiter->autoBlacklistThreshold ?> attempts</li>
|
||||
<li>Auto-blacklist duration: <?= $rateLimiter->autoBlacklistDuration ?> hours</li>
|
||||
</ul>
|
||||
<p class="mb-0">
|
||||
<small>Note: These settings can be modified in the RateLimiter class configuration.</small>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h4>Recent Failed Login Attempts</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Username</th>
|
||||
<th>Attempted At</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$stmt = $rateLimiter->db->prepare("
|
||||
SELECT ip_address, username, attempted_at
|
||||
FROM {$rateLimiter->ratelimitTable}
|
||||
ORDER BY attempted_at DESC
|
||||
LIMIT 10
|
||||
");
|
||||
$stmt->execute();
|
||||
$attempts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($attempts as $attempt) {
|
||||
?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($attempt['ip_address']) ?></td>
|
||||
<td><?= htmlspecialchars($attempt['username']) ?></td>
|
||||
<td><?= htmlspecialchars($attempt['attempted_at']) ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<!-- /Security Settings -->
|
|
@ -1,13 +1,16 @@
|
|||
INSERT OR IGNORE INTO rights VALUES(1,'superuser');
|
||||
INSERT OR IGNORE INTO rights VALUES(2,'edit users');
|
||||
INSERT OR IGNORE INTO rights VALUES(3,'view config file');
|
||||
INSERT OR IGNORE INTO rights VALUES(4,'edit config file');
|
||||
INSERT OR IGNORE INTO rights VALUES(5,'view own profile');
|
||||
INSERT OR IGNORE INTO rights VALUES(6,'edit own profile');
|
||||
INSERT OR IGNORE INTO rights VALUES(7,'view all profiles');
|
||||
INSERT OR IGNORE INTO rights VALUES(8,'edit all profiles');
|
||||
INSERT OR IGNORE INTO rights VALUES(9,'view app logs');
|
||||
INSERT OR IGNORE INTO rights VALUES(10,'view all platforms');
|
||||
INSERT OR IGNORE INTO rights VALUES(11,'edit all platforms');
|
||||
INSERT OR IGNORE INTO rights VALUES(12,'view all agents');
|
||||
INSERT OR IGNORE INTO rights VALUES(13,'edit all agents');
|
||||
INSERT OR IGNORE INTO rights (`id`, `name`) VALUES(1,'superuser');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit users');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view config file');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit config file');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view own profile');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit own profile');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view all profiles');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit all profiles');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view app logs');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view all platforms');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit all platforms');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('view all agents');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit all agents');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit whitelist');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit blacklist');
|
||||
INSERT OR IGNORE INTO rights (`name`) VALUES('edit ratelimiting');
|
||||
|
|
|
@ -44,6 +44,7 @@ $allowed_urls = [
|
|||
'config',
|
||||
'status',
|
||||
'logs',
|
||||
'security',
|
||||
'help',
|
||||
|
||||
'login',
|
||||
|
@ -148,6 +149,29 @@ if ($page == 'logout') {
|
|||
include '../app/templates/block-message.php';
|
||||
include '../app/pages/login.php';
|
||||
|
||||
} elseif ($page === 'security') {
|
||||
// Security settings require login
|
||||
if (!isset($currentUser)) {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get user details and rights
|
||||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||
$userDetails = $userObject->getUserDetails($user_id);
|
||||
$userRights = $userObject->getUserRights($user_id);
|
||||
$userTimezone = isset($userDetails[0]['timezone']) ? $userDetails[0]['timezone'] : 'UTC';
|
||||
|
||||
// Initialize RateLimiter
|
||||
require_once '../app/classes/ratelimiter.php';
|
||||
$rateLimiter = new RateLimiter($dbWeb);
|
||||
|
||||
include '../app/templates/page-header.php';
|
||||
include '../app/templates/page-menu.php';
|
||||
include '../app/templates/page-sidebar.php';
|
||||
include '../app/pages/security.php';
|
||||
include '../app/templates/page-footer.php';
|
||||
|
||||
} else {
|
||||
|
||||
// if user is logged in, we need user details and rights
|
||||
|
|
Loading…
Reference in New Issue