Настройка мандатного управления доступом(МРД) с Kerberos аутентификацией в ЕПП, Apache2, Postgres#
Исходные данные#
Имеется сервер контроллера домена FreeIPA:
имя домена astra.aaa
администратор домена admin@astra.aaa
пользователь домена user-01@astra.aaa
имя сервера dc-01.astra.aaa
сервер имеет постоянный IP-адрес, например, 192.168.1.20
Веб-сервер располагается отдельно:
имя сервера websrv-01.astra.aaa
сервер должен быть введен в домен
на сервере установлен и настроен web-сервер Apache2
сервер имеет постоянный IP-адрес, например, 192.168.1.21
Сервер базы данных располагается отдельно:
имя сервера dbsrv-01.astra.aaa
сервер должен быть введен в домен
на сервере должна быть установлена и настроенная СУБД Postgresql
сервер имеет постоянный IP-адрес, например, 192.168.1.23
Пользовательский компьютер располагается на отдельном компьютере:
имя компьютера pc-01.astra.aaa
компьютер должен быть введен в домен
компьютер имеет постоянный IP-адрес, например, 192.168.1.22
Основная концепция, реализация веб-приложения на языке программирования PHP с МРД#
Контроллер домена Freeipa, веб сервер Apache2 и СУБД Postgres предварительно настроены для использования МРД и kerberos аутентификации.
Пользователь домена при входе в сеанс на своём компьютере выбирает уровень и категорию из доступных для него, далее при отправке запроса из браузера веб сервер Apache2 получает классификационную метку пользователя и его билет kerberos.
Веб сервер Apache2 производит аутентификацию пользователя и если она прошла успешно, то обработчик запроса переключается в контекст пользователя, включая классификационную метку МРД его сеанса.
Далее запускается приложение и создается делегируемый kerberos кэш. Если аутентификации прошла неуспешно, выдается ошибка.
Далее веб сервер Apache2 передаёт запрос веб приложению PHP.
Веб приложение PHP добавляет в окружение переменную KRB5CCNAME.
Далее коннектор php-pgsql, в режиме GSS, производит запрос к БД Postgresql с передачей контекста пользователя, включая классификационную метку МРД.
СУБД Postgres так же производит kerberos аутентификацию, авторизацию по правилам МРД и возвращает результаты запроса.
Настройка контроллера домена#
Для настройки контроллера домена необходимо перейти по ссылке и выполнить действия по инструкции:
Настройка компьютера пользователя#
Для настройки компьютера пользователя необходимо перейти по ссылке и выполнить действия по инструкции:
Настройка сервера базы данных#
Для настройки сервера базы данных необходимо перейти по ссылке и выполнить действия по инструкции:
Установка и настройка веб-сервера Apache2#
Для установки и настройки веб-сервера Apache2 необходимо перейти по ссылке и выполнить действия по инструкции:
Установка веб-приложения#
Для установки и настройки веб-приложения необходимо:
Пункт 1#
установить следующие пакеты пакет:
sudo apt install php php-pgsql
Пункт 2#
создать файл
index.phpпо пути/var/www/public_html:
sudo nano /var/www/public_html/index.php
Пункт 3#
файл
index.phpдолжен иметь следующее содержимое:
<?php
// Конфигурация подключения к БД
putenv("KRB5CCNAME=" . $_SERVER['KRB5CCNAME']);
$dbConfig = [
'host' => 'test181-dbsrv-01.astra.bbb',
'port' => '5433',
'dbname' => 'demoprimer',
];
// Подключение к базе данных
try {
$dsn = "pgsql:host={$dbConfig['host']};port={$dbConfig['port']};dbname={$dbConfig['dbname']}";
$db = new PDO($dsn, '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Ошибка подключения к базе данных: " . $e->getMessage());
}
// Переменные для сообщений
$operation = isset($_GET['operation']) ? $_GET['operation'] : null;
$message = '';
// Обработка удаления записи ДО получения списка записей
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['delete'])) {
try {
$stmt = $db->prepare("DELETE FROM s1.t1 WHERE id = ?");
$stmt->execute([$_GET['delete']]);
// Перенаправление с параметром операции
header("Location: ".strtok($_SERVER['REQUEST_URI'], '?')."?operation=deleted");
exit();
} catch (PDOException $e) {
die("Ошибка при удалении записи: " . $e->getMessage());
}
}
// Обработка остальных CRUD операций
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
session_start();
$currentUser = $_SERVER['REMOTE_USER'];
if (isset($_POST['create'])) {
// Создание новой записи
$stmt = $db->prepare("INSERT INTO s1.t1 (insert_user, insert_date, classificator)
VALUES (?, CURRENT_TIMESTAMP, ?)");
$stmt->execute([$currentUser, $_POST['classificator']]);
// Перенаправление с параметром операции
header("Location: ".$_SERVER['PHP_SELF']."?operation=created");
exit();
} elseif (isset($_POST['update'])) {
// Обновление записи
$stmt = $db->prepare("UPDATE s1.t1
SET classificator = ?
WHERE id = ?");
$stmt->execute([$_POST['classificator'], $_POST['id']]);
// Перенаправление с параметром операции
header("Location: ".$_SERVER['PHP_SELF']."?operation=updated");
exit();
}
}
// Получение всех записей
$records = $db->query("SELECT maclabel, *, extract(epoch from insert_date) as insert_ts
FROM s1.t1
ORDER BY insert_date DESC")->fetchAll(PDO::FETCH_ASSOC);
// Получение записи для редактирования
$editRecord = null;
if (isset($_GET['edit'])) {
$stmt = $db->prepare("SELECT * FROM s1.t1 WHERE id = ?");
$stmt->execute([$_GET['edit']]);
$editRecord = $stmt->fetch(PDO::FETCH_ASSOC);
}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Демонстрационный пример</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }
h1, h2 { margin: 0 0 20px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; border-radius: 8px; overflow: hidden; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
th, td { padding: 12px 15px; text-align: left; border: 1px solid #ddd; }
th { background-color: #f2f2f2; font-weight: 600; }
tr:nth-child(even) { background-color: #f9f9f9; }
tr:hover { background-color: #f1f1f1; }
form { margin-bottom: 20px; padding: 20px; background: #f9f9f9; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.05); }
label { display: block; margin-bottom: 8px; font-weight: 500; }
input { margin-bottom: 15px; width: 100%; max-width: 500px; padding: 10px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; box-sizing: border-box; }
input:focus { outline: none; border-color: #0079C1; box-shadow: 0 0 5px rgba(0,121,193,0.3); }
button { background: #0079C1; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; }
.button { padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; text-decoration: none; display: inline-block; margin-right: 8px; color: white; }
button:hover { background: #00588D; }
.actions { white-space: nowrap; }
.uuid { font-family: monospace; font-size: 0.9em; }
.form-row { margin-bottom: 15px; }
.button-cancel { background: #6c757d; }
.button-cancel:hover { background: #5a6268; }
.button-edit { background: #0079C1; }
.button-edit:hover { background: #00588D; }
.button-delete { background: #dc3545; }
.button-delete:hover { background: #bb2d3b; }
/* Стили для toast-уведомлений */
.toast {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 4px;
color: white;
background-color: #28a745;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
.toast.show {
opacity: 1;
}
.toast.error {
background-color: #dc3545;
}
</style>
</head>
<body>
<h1>Демонстрационный пример</h1>
<!-- Toast-уведомление -->
<div id="toast" class="toast"></div>
<!-- Форма для создания/редактирования -->
<form method="POST">
<h2><?= $editRecord ? 'Редактировать запись' : 'Добавить новую запись' ?></h2>
<?php if ($editRecord): ?>
<input type="hidden" name="id" value="<?= htmlspecialchars($editRecord['id']) ?>">
<?php endif; ?>
<div class="form-row">
<label for="classificator">Наименование:</label>
<input type="text" id="classificator" name="classificator" placeholder="Введите наименование" required
value="<?= htmlspecialchars($editRecord['classificator'] ?? '') ?>">
</div>
<div class="form-row">
<?php if ($editRecord): ?>
<button type="submit" name="update">Обновить</button>
<a href="?" class="button button-cancel">Отмена</a>
<?php else: ?>
<button type="submit" name="create">Создать</button>
<?php endif; ?>
</div>
</form>
<!-- Таблица с записями -->
<table>
<thead>
<tr>
<th>ID</th>
<th>MAC</th>
<th>Наименование</th>
<th>Создано</th>
<th>Кем создано</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<?php foreach ($records as $record): ?>
<tr>
<td><?= htmlspecialchars($record['id']) ?></td>
<td><?= htmlspecialchars($record['maclabel']) ?></td>
<td><?= htmlspecialchars($record['classificator']) ?></td>
<td>
<div><?= date('d-m-Y H:i:s', $record['insert_ts']) ?></div>
</td>
<td><?= htmlspecialchars(strtolower($record['insert_user'])) ?></td>
<td class="actions">
<a href="?edit=<?= urlencode($record['id']) ?>" class="button button-edit">Редактировать</a>
<a href="?delete=<?= urlencode($record['id']) ?>" class="button button-delete" onclick="return confirm('Вы уверены?')">Удалить</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<script>
// Функция для показа toast-уведомления
function showToast(message, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = isError ? 'toast error show' : 'toast show';
setTimeout(() => {
toast.className = 'toast';
}, 3000);
}
// Проверка параметра operation в URL и показ соответствующего сообщения
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const operation = urlParams.get('operation');
if (operation === 'created') {
showToast('Запись успешно создана');
} else if (operation === 'updated') {
showToast('Запись успешно обновлена');
} else if (operation === 'deleted') {
showToast('Запись успешно удалена');
}
// Удаляем параметр operation из URL без перезагрузки страницы
if (operation) {
const cleanUrl = window.location.protocol + '//' + window.location.host + window.location.pathname;
window.history.replaceState({}, document.title, cleanUrl);
}
});
</script>
</body>
</html>
Сценарий проверки работы МРД в веб-приложении#
Важно
Документация дорабатывается по мере развития продуктов Группы Астра и по пожеланиям пользователей.
Ваши пожелания и замечания направляйте на почту docs@astralinux.ru