Настройка мандатного управления доступом(МРД) с 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());
}
// Обработка удаления записи ДО получения списка записей
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'], '?'));
        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']]);
    } 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']);
    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; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; text-decoration: none; display: inline-block;>
        .button { padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500; text-decoration: none; display: inline-block; margin-right: 8px; >
        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; color: #fff; }
        .button-edit:hover { background: #00588D; }
        .button-delete { background: #dc3545; }
        .button-delete:hover { background: #bb2d3b; }
    </style>
</head>
<body>
    <h1>Демонстрационный пример</h1>
    <!-- Форма для создания/редактирования -->
    <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>
</body>
</html>

Сценарий проверки работы МРД в веб-приложении#

Важно

Документация дорабатывается по мере развития продуктов Группы Астра и по пожеланиям пользователей.

Ваши пожелания и замечания направляйте на почту docs@astralinux.ru