dojoConfig
В Dojo Toolkit все базовые настройки приложения хранятся в глобальном объекте dojoConfig, и его инициализация должна происходить раньше, чем будет подключена сама библиотека Dojo, потому что при загрузке выполняется код, который проверяет существование этой переменной. В итоге пришёл к вот такому конфигу:
(function(G) {
"use strict"; // Возможно, избыточно, поскольку весь Dojo написан без этой директивы
G.dojoConfig = {
async: true, // Для всех браузеров должно быть так
parseOnLoad: false, // Разбор страницы на виджеты при загрузке? Лучше дождаться domReady
debug: true,
// Самая сложная часть - определение текущей локали браузера. Влияет на всякие
// внутренние механизмы Dojo, например, какой язык будет использован для виджетов.
// Хотите календарь на русском языке? Первый день недели - понедельник? Это сюда.
// Можно и явно указать "ru-ru", но мне нравится вот такой подход.
locale: navigator.language || navigator.userLanguage
};
})(this); // this === window
Dgrid: 100% высоты родителя, проблемы рендеринга и totalLength
Я довольно часто пользуюсь Dgrid. Это довольно мощная таблица, однако, у нее есть ряд недостатков, которые решаются обходными путями, например, через редактирование стилей. Вот так можно задать автоматическое растягивание таблицы по размеру родительского блока:
.dgrid {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: auto;
}
Решение проблемы взято со StackOverflow.
Но есть другая проблема. Допустим, мы решили разместить DGrid внутри компонента типа ContentPane. При отображении заголовок будет сжат в 0 и наедет на данные. Можно изменить размер окна браузера, чтобы отображалось нормально, а можно:
var cpWithDgrid = new ContentPane({
buildRendering: function() {
this.inherited(arguments);
this.grid = new DGrid({}); // Подставьте свой класс для Grid'а
this.set("content", this.grid);
},
resize: function() {
this.inherited(arguments);
this.grid.resize();
}
});
А разгадка такого поведения очень проста: высота элементов таблицы DGrid вычисляется до помещения в DOM, а потому они получают 0 в качестве значения. DGrid, разумеется, под суд. По-хорошему, при запуске вашего виджета он должен всем дочерним виджетам дать команду пересчитать размеры, но конкретно с DGrid это почему-то не работает. Костыль? Костыль. Но работает.
Надо ещё про работу с Rest сказать. Мои таблицы получают данные с сервера. В поле collection в качестве значения я указываю экземпляра класса-наследника dstore/Rest. Самое интересное, что при получении данных с сервера в логах часто можно увидеть сообщение об ошибке: Store reported null or undefined totalLength. Make sure your store (and service, if applicable) are reporting total correctly! Разумеется, это не правда. На самом деле DGrid ожидает увидеть в ответе сервера поле total, а не totalLength.
Поскольку на сервере у меня обычно Django + Django REST Framework, нужно соблюдать его требования. Одним из них является завершающий слеш в конце любого запроса. В итоге создал миксин, с которым смешиваю все создаваемые мной классы-наследники dstore/Rest:
define([
"dojo/_base/declare"
], function(declare) {
return declare(null, {
ascendingPrefix: "",
_getTarget: function(id) {
// По требованиям DRF в конце URL должен стоять /
var target = this.target;
// А можно решить с помощью строковых литералов {$id}, но не буду
if (target.slice(-1) == '/') {
return target + id + '/';
} else {
return target + '/' + id + '/';
} // fi
} // _getTarget
}); // declare
}); // define
CSRF-Token в Dojo Toolkit 1.x и Django
Постоянно забываю, как в Dojo Toolkit 1.x автоматически цеплять CSRF-Token к XHR-запросам. Ниже просто код, который должен запускаться при старте приложения (FrontEnd).
require([
"dojo/cookie",
"dojo/request/notify",
"dojo/domReady!"
], function(cookie, notify) {
notify("send", function(response, cancel) {
response.xhr.setRequestHeader("X-SCRFToken", cookie("csrftoken"));
});
});
И
csrf перенесена в модуль django.template.context_processors# -*- coding: utf-8 -*-
u"""Набор видов для построения базового интерфейса приложения."""
from django.shortcuts import render_to_response
from django.views.generic import View
from django.core.context_processors import csrf
class Index(View):
u"""Главное окно приложения."""
template_name = "index.html"
def get(self, request):
# Установка CSRF-токена
c = {}
c.update(csrf(request))
return render_to_response(self.template_name, c)
Но этого мало. Нужно ещё в теле главной страницы разместить скрытое поле, куда будет записан токен. Кстати, только так в старых версиях Django можно указать cookie для его хранения.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8"/>
<title>Установка CSRF-Token'а</title>
</head>
<body>
<!-- ТО САМОЕ СКРЫТОЕ ПОЛЕ -->
<input name="csrftoken" type="hidden" value="{% csrf_token %}" />
<!-- /ТО САМОЕ СКРЫТОЕ ПОЛЕ -->
</body>
</html>
Запуск Django-приложений через mod_python в Astra Linux SE
Запуск Python-Web-приложений в Astra Linux
В Astra Linux до версии 1.6 не было иного способа нормального запуска Web-приложений, написанных на Python, кроме mod_python. В настоящее время самый популярный способ - WSGI, однако, в сертифицированных версиях Astra Linux SE (1.4, 1.5) есть только указанный выше модуль для Apache. Безусловно, есть и третий способ - запуск приложения в отладочном режиме через вот эту команду:
python manage.py runserver
Надо ли объяснять, что делать так не нужно?
Итак, задачи, решение которых я предлагаю в этой статье:
- Настройка Apache для работы в рамках ALD (Astra Linux Domain)
- Настройка виртуального хоста для Django-приложения
- Авторизация в приложении Django через ALD/Kerberos
Настройка ALD и Apache
Установка необходимых модулей Apache.
Устанавливаем два модуля Apache (считаем, что Apache уже установлен на сервере).
apt-get install libapache2-auth-mod-kerb libapache2-mod-python
Включаем эти модули и перезапускаем WEB-сервер:
a2dismod auth_pam
a2enmod python
a2enmod auth_kerb
Настройка ALD.
Теперь нужно настроить сервер таким образом, чтобы клиенты, подключающиеся в рамках сессии ALD, прозрачно авторизовались на сервере. В инструкции всё описано, лишь немного постараюсь прояснить моменты, которые у меня вызвали вопросы.
Создадим принципала в ALD и добавим его в группу mac
ald-admin service-add HTTP/server.domain.lan
ald-admin sgroup-svc-add HTTP/server.domain.lan --sgroup=mac
Теперь создадим файл ключа Kerberos и дадим права на него пользователю www-data.
KEYTAB="/etc/apache2/keytab"
ald-client update-svc-keytab HTTP/server.domain.lan --ktfile=$KEYTAB
chown www:data $KEYTAB
chmod 644 $KEYTAB
Теперь можно перезапустить WEB-сервер:
service apache2 restart
Создание виртуального хоста Apache.
Подготовительные операции выполнены, на клиентах Firefox настроен на использование GSS API для авторизации (about:config, потом в параметр network.negotiate-auth.delegation-uris вписываем http://,https://). Самое время создать виртуальный хост для нашего Django-проекта, лежащего в каталоге /var/www/site/.
<VirtualHost *:80>
ServerName server.domain.lan
ServerAdmin useradmin@domain.lan
DocumentRoot /var/www/site
AddDefaultCharset utf-8
<Directory "/var/www/site/”>
Options -Indexes FollowSymLinks -MultiViews
AllowOverride None
AuthType Kerberos
KrbAuthRealms DOMAIN.LAN
KrbServiceName HTTP/server.domain.lan
Krb5Keytab /etc/apache2/keytab
KrbMethodNegotiate on
KrbMethodK5Passwd off
KrbSaveCredentials on
require valid-user
Order deny,allow
Allow from all
</Directory>
<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE site.settings
PythonOption diango.root /var/www/site
PythonPath "['/var/www/site/',] + sys.path"
PythonAutoReload On
</Location>
<Location "/media/”>
SetHandler None
</Location>
<Location "/static/">
SetHandler None
</Location>
<LocationMatch "\.(jpg|gif|png)$">
SetHandler None
</Location>
ErrorLog /var/www/site/log/error.log
LogLevel warn
SetEnfIf Request_URI "\.jpg$|\.gif$|\.css$|\.js" is_static
CustomLog /var/www/site/log/access.log combined env=!is_static # Убрать лишнее из логов доступа, например, статику
</VirtualHost>
Не забываем включить наш сайт в список разрешенных:
a2ensite astra-django-project
service apache2 reload
Побочные эффекты
Возможно, у меня руки кривые, или я чего-то не знаю, но есть некоторые факты, которым я не нахожу другого объяснения, например, откусывание заголовков HTTP сервером Apache. Допустим, у нас есть вот такой код:
# -*- coding: utf-8 -*-
u"""Виды для обработки данных абонентов."""
from django.shortcuts import get_object_or_404
from django.views.generic import View
from abonent.models import Abonent
from rest.responses import JsonResponse
from .serializers import AbonentSimpleSerializer
class AbonentRootView(View):
def get(self, request):
u"""
Именно здесь происходит сериализация модели в JSON.
Возвращаемый объект - JsonResponse.
"""
root_abonent = Abonent.objects.filter(parent=None)[0]
serializer = AbonentSimpleSerializer()
response = JsonResponse({
"items": [
serializer.serialize(root_abonent),
],
"total": 1
})
# Тут задаем заголовки, чтобы библиотека dgrid могла с ними работать
# Не надо смотреть на цифры, они сейчас не имеют значения (просто пример)
response["Content-Range"] = "items: 1-1/1"
return response
Не суть важно, как происходит сериализация (сериализатор возвращает словарь), важно то, что происходит в строке, где устанавливается заголовок ответа Content-Range. Если запустить отладочный сервер, то в заголовках ответа мы его увидим. Если выполнять этот же код с помощью Apache, т. е. так, как я выше написал, заголовок будет просто выброшен. Как это лечить в Astra Linux 1.4, я не знаю. Но, например, при использовании библиотеки DStore использовать свойство useRangeHeaders будет нельзя. Т. е. и DGrid тоже работать не будет, нужно помимо свойства items передавать ещё и total. Не такая уж большая проблема, но под определение подводного камня подходит хорошо.
Elpy, python-mode и use-package
Emacs - отличный редактор для тех, кто пишет на Python, и есть куча пакетов для поддержки этого языка. Я уже раньше писал, что теперь храню свою конфигурацию и управляю пакетами с помощью use-package, однако, долго не мог найти способ для красивого скрещивания замечательного пакета elpy cо стандартным python-mode.
Решение оказалось не очень сложным, хотя на его поиск пришлось потратить некоторое время. Ниже выкладываю кусок своего init.el.
(use-package python-mode
:mode ("\\.py\\'" . python-mode)
:init(add-hook 'python-mode-hook #'elpy-enable)
:config
(use-package elpy
:bind
("M-," . elpy-goto-definition)
:init
(elpy-enable)
(defalias 'workon 'pyvenv-workon)
:config
(add-to-list 'company-backends 'elpy-company-backend)
(elpy-enable))
(use-package py-autopep8
:hook
(python-mode . py-autopep8-enable-on-save))
(use-package py-isort
:init
(add-hook 'before-save-hook #'py-isort-before-save)))
Этот код не делает никакой магии. В общем-то, тут вообще нет ничего интересного.
Самому себе оставляю памятку: в настройках settings.el уже включена автоматическая загрузка недостающих пакетов с помощью use-package. Для всех Python'ьих файлов как major-mode указан python-mode. При его инициализации дополнительно подключаются пакеты elpy, py-autopep8 и py-isort, почти все настройки которых уже вынесены сами знаете куда. Кроме того, каждый пакет при загрузке ставит хуки на определенное действие. Например, автоматическое форматирование или упорядочение импортированных модулей.
Не уверен в том, какую строку с elpy-enable можно удалить, чтобы ничего не сломалось. Сегодня выяснять лень, потом разберусь.
Красивый способ хранения настроек Emacs
Интересный подход к хранению конфигурации Emacs
Интересный способ хранения конфигурации подсмотрел у автора use-package. Если вкратце, то все настройки для конкретных пакетов вынесены в файл settings.el, а вот список используемых пакетов и нюансы взаимодействия между ними описаны целиком и полностью в init.el.
Чтобы далеко не ходить, вот пример кода:
;;; init.el --- Summary
;;; Commentary:
;;; Main EMACS settings file, load settings from parts.
;;; Code:
;; Added by Package.el. This must come before configurations of
;; installed packages. Don't delete this line. If you don't want it,
;; just comment it out by adding a semicolon to the start of the line.
;; You may delete these explanatory comments.
(package-initialize)
(eval-when-compile(require 'cl))
(require 'package)
(setq package-archives nil)
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)
(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives '("elpy" . "https://jorgenschaefer.github.io/packages/") t)
(setq package-enable-at-startup nil)
(package-initialize nil)
;;; Загрузка и установка пакета use-package, если его нет в системе
(unless(package-installed-p 'use-package)
(message "EMACS install use-package.el")
(package-refresh-contents)
(package-install 'use-package))
(require 'use-package)
;;; Шрифты
(set-face-attribute 'default nil :height 110)
(when (member "DejaVu Sans Mono" (font-family-list))
(set-face-attribute 'default nil :font "DejaVu Sans Mono"))
;;; Заставляю хоткеи работать и в русской раскладке
(defun cfg:reverse-input-method (input-method)
"Build the reverse mapping of single letters from INPUT-METHOD."
(interactive
(list (read-input-method-name "Use input method (default current): ")))
(if (and input-method (symbolp input-method))
(setq input-method (symbol-name input-method)))
(let ((current current-input-method)
(modifiers '(nil (control) (meta) (control meta))))
(when input-method
(activate-input-method input-method))
(when (and current-input-method quail-keyboard-layout)
(dolist (map (cdr (quail-map)))
(let* ((to (car map))
(from (quail-get-translation
(cadr map) (char-to-string to) 1)))
(when (and (characterp from) (characterp to))
(dolist (mod modifiers)
(define-key local-function-key-map
(vector (append mod (list from)))
(vector (append mod (list to)))))))))
(when input-method
(activate-input-method current))))
(cfg:reverse-input-method 'russian-computer)
;; Resize windows
(global-set-key(kbd "S-C-") 'shrink-window-horizontally)
(global-set-key(kbd "S-C-") 'enlarge-window-horizontally)
(global-set-key(kbd "S-C-") 'shrink-window)
(global-set-key(kbd "S-C-") 'enlarge-window)
;; Isearch
(global-set-key (kbd "C-M-r") 'isearch-backward-other-window)
(global-set-key (kbd "C-M-s") 'isearch-forward-other-window)
(defun xah-new-empty-buffer()
"Open a new empty buffer.
URL `http: // ergoemacs.org / emacs / emacs_new_empty_buffer.html'
Version 2015 - 06 - 12"
(interactive)
(let((ξbuf(generate-new-buffer "untitled")))
(switch-to-buffer ξbuf)
(funcall(and initial-major-mode))
(setq buffer-offer-save t)))
;; Save/close/open
(global-set-key(kbd "C-w") 'kill-this-buffer)
(global-set-key(kbd "C-s") 'save-buffer)
(global-set-key(kbd "C-S-s") 'write-file)
(global-set-key(kbd "C-r") 'revert-buffer)
(global-set-key(kbd "C-a") 'mark-whole-buffer)
(global-set-key(kbd "M-'") 'comment-or-uncomment-region)
(global-set-key(kbd "C-o") 'dired)
(global-set-key(kbd "C-n") 'xah-new-empty-buffer)
(global-set-key(kbd "C-+") 'text-scale-increase)
(global-set-key(kbd "C--") 'text-scale-decrease)
;; Buffers and windows
(global-set-key(kbd "C-") 'next-buffer)
(global-set-key(kbd "C-") 'previous-buffer)
(global-set-key(kbd "C-") 'other-window)
(global-set-key(kbd "M-3") 'delete-other-windows)
(global-set-key(kbd "M-4") 'split-window-horizontally)
(global-set-key(kbd "M-5") 'split-window-vertically)
(global-set-key(kbd "M-6") 'balance-windows)
(global-set-key(kbd "C-f") 'isearch-forward)
(global-set-key(kbd "C-h") 'query-replace)
(global-set-key(kbd "C-S-h") 'query-replace-regexp)
(global-set-key(kbd "M-a") 'execute-extended-command)
(global-set-key(kbd "M-x") 'kill-whole-line)
(global-set-key(kbd "") 'keyboard-quit)
(when(get-buffer "*scratch*")
(kill-buffer "*scratch*"))
(fset 'yes-or-no-p 'y-or-n-p); ; ; Shortcuts for yea and no
;;; Format file before save
(defun format-current-buffer()
(indent-region (point-min) (point-max)))
(defun untabify-current-buffer()
(if (not indent-tabs-mode)
(untabify (point-min) (point-max)))
nil)
(add-to-list 'write-file-functions 'untabify-current-buffer)
(add-to-list 'write-file-functions 'delete-trailing-whitespace)
;;; САМЫЙ ВАЖНЫЙ МОМЕНТ - ВЫНЕСЕНИЕ НАСТРОЕК СРЕДЫ В settings.el и загрузка их оттуда
(setq custom-file "~/.emacs.d/settings.el")
(load-file "~/.emacs.d/settings.el")
;;; Теперь просто описание используемых пакетов
;;; Настройки в большинстве случаев подтягиваются из файла settings.el
(use-package airline-themes
:requires powerline
:init
(load-theme 'airline-molokai))
(use-package all-the-icons)
(use-package beacon
:diminish
:commands beacon-mode)
(use-package company
:diminish
:commands company-mode
:config
;; From https://github.com/company-mode/company-mode/issues/87
;; See also https://github.com/company-mode/company-mode/issues/123
(defadvice company-pseudo-tooltip-unless-just-one-frontend
(around only-show-tooltip-when-invoked activate)
(when (company-explicit-action-p)
ad-do-it))
(defun check-expansion ()
(save-excursion
(if (outline-on-heading-p t)
nil
(if (looking-at "\\_>") t
(backward-char 1)
(if (looking-at "\\.") t
(backward-char 1)
(if (looking-at "->") t nil))))))
(define-key company-mode-map [tab]
'(menu-item "maybe-company-expand" nil
:filter (lambda (&optional _)
(when (check-expansion)
#'company-complete-common))))
(eval-after-load "yasnippet"
'(progn
(defun company-mode/backend-with-yas (backend)
(if (and (listp backend) (member 'company-yasnippet backend))
backend
(append (if (consp backend) backend (list backend))
'(:with company-yasnippet))))
(setq company-backends
(mapcar #'company-mode/backend-with-yas company-backends))))
:init (global-company-mode t))
(use-package company-quickhelp
:bind
(:map company-active-map
("C-c h" . company-quickhelp-manual-begin)))
(use-package css-mode
:mode "\\.css\\'")
(use-package elpy
:mode "\\.py\\'"
:requires python-mode
:hook (add-hook 'before-save-hook #'elpy-format-code)
:init
(elpy-enable)
(defalias 'workon 'pyvenv-workon))
(use-package emmet-mode
:mode ("\\.html\\'" . emmet-mode)
:bind
("C-j" . emmet-expand-line))
(use-package flycheck
:commands flycheck-mode
:init(add-hook 'after-init-hook #'global-flycheck-mode))
(use-package highlight-numbers
:hook(prog-mode . highlight-numbers-mode))
(use-package ibuffer
:bind([f2] . ibuffer)
:init
(add-hook 'ibuffer-mode-hook #'(lambda ()(ibuffer-switch-to-saved-filter-groups "default"))))
(use-package js2-mode
:mode ("\\.js\\'" . js2-mode)
:requires flycheck
:bind(
:map js2-mode-map
("M-n" . flycheck-next-error)
("M-p" . flycheck-previous-error))
:config
(add-to-list 'flycheck-disabled-checkers #'javascript-jshint)
(flycheck-add-mode 'javascript-eslint 'js2-mode)
(flycheck-mode 1)
(js2-highlight-unused-variables-mode t))
(use-package json-mode
:mode (("\\.json\\'" . json-mode)
("\\.bowerrc\\'" . json-mode)
("\\.jshintrc\\'" . json-mode)))
(use-package json-reformat
:after json-mode)
(use-package magit
:bind([f5] . magit-status))
(use-package markdown-mode
:mode "\\.md\\'")
(use-package monokai-theme
:init(load-theme 'monokai))
(use-package neotree
:requires all-the-icons
:bind
([f8] . neotree-toggle))
(use-package powerline)
(use-package py-isort
:hook
(add-hook 'before-save-hook 'py-isort-before-save))
(use-package rainbow-delimiters
:hook
(prog-mode . rainbow-delimiters-mode))
(scroll-bar-mode -1)
(use-package tide
:requires js2-mode
:after js2-mode
:mode ("\\.js\\'" . tide-mode)
:hook((before-save-hook . tide-format-before-save)
(typescript-mode-hook . setup-tide-mode)))
(use-package web-beautify)
(use-package web-mode
:commands web-mode
:requires web-beautify
:mode("\\.phtml\\'" . web-mode)
("\\.html\\'" . web-mode)
:hook (add-hook 'before-save-hook 'web-beautify-html-buffer t t))
(use-package yasnippet
:after prog-mode
:defer 10
:diminish yas-minor-mode
:mode("/\\.emacs\\.d/snippets/" . snippet-mode)
:config
(yas-load-directory (expand-file-name "snippets" user-emacs-directory))
(yas-global-mode 1))
(use-package yasnippet-snippets
:after yasnippet)
;;; init.el ends here
Что интересного я взял у автора?
Во-первых, саму организацию хранения описания пакетов. Во-вторых, он отсортировал вызовы use-package по алфавиту. Быстрый поиск не нужен, если можно просто нажать пару раз [PgDn]. В-третьих, он и в файле настроек применил сортировку по алфавиту.
Для тех, кому интересен оригинальный конфиг, его можно найти здесь.
Настройка DNS-сервера bind9 в Debian/Astra Linux
Цели
- Настройка основного сервера DNS для маленькой локальной сети
- Сервер должен быть безопасным
- Сервер должен хранить настройки для конфигурации данной сети
- Сервер должен обслуживать доменную зону
home.netс адресами в подсети192.168.0/24
Как обычно, если не указано иное, команды выполняются от имени пользователя root.
Исходные данные
| Доменная зона | home.net |
| Подсеть | 192.168.0/24 |
| Имя основного сервера | dc.home.net |
| IP-адрес сервера имен | 192.168.0.1 |
| Имя первого хоста | arm01.home.net |
| IP-адрес первого хоста | 192.168.0.101 |
| Имя N-го хоста | armN.home.net |
| IP-адрес N-го хоста | 192.168.0.(100+N) |
Установка сервера
apt-get install bind9 -y
Сервер установлен, но установка сделана не безопасным способом. Нужно выполнить несколько дополнительных действий, описанных подробнее в Debian Wiki - Bind.
Будем запускать сервер в изолированном окружении:
mkdir -p /var/bind9/chroot/{etc,dev,var/cache/bind,var/run/named}
mknod /var/bind9/chroot/dev/null c 1 3
mknod /var/bind9/chroot/dev/random c 1 8
chmod 660 /var/bind9/chroot/dev/{null,random}
mv /etc/bind /var/bind9/chroot/etc
ln -s /var/bind9/chroot/etc/bind /etc/bind
chown bind:bind /var/bind9/chroot/etc/bind/rndc.key
chmod 775 /var/bind9/chroot/var/{cache/bind,run/named}
chgrp bind /var/bind9/chroot/var/{cache/bind,run/named}
Теперь нужно изменить расположение PIDFILE. Для этого в файле /etc/init.d/bind9 нужно переопределить переменную PIDFILE.
PIDFILE=/var/bind9/chroot/var/run/named/named.pid
Чтобы сервер понимал, что его запускают в изолированном окружении, нужно изменить параметры его запуска, внеся небольшие коррективы в файл /etc/default/bind9
OPTIONS="-u bind -t /var/bind9/chroot"
Перенастроим логирование rsyslog
echo "\$AddUnixListenSocket /var/bind9/chroot/dev/log" > /etc/rsyslog.d/bind-chroot.conf
Настройка опций сервера
Безопасность
Основные настройки сервера хранятся в файле /etc/bind/named.conf.options (на самом деле сейчас это всего лишь символическая ссылка, но на суть дела это не влияет). Добавим пару параметров для пущей безопасности в разделе options:
options {
...
version none; // Скроем версию bind
allow-query {
192.168.0/24; // Отвечать будем только на запросы, приходящие из внутренней сети
};
...
}
Включение в конфигурацию новой доменной зоны
В файле /etc/bind/named.conf.default-zones добавим указание на файлы прямой и обратной зоны, чтобы сервер мог обслуживать локальную сеть:
...
zone "home.net" in { // Локальная сеть
type master;
file "/etc/bind/db.home.net"; // В этом файле будут адреса прямой зоны
};
zone "0.168.192.in-addr.arpa" { // Так описывается обратная зона, т. е. первые три октета
// записываются в обратном порядке и добавляется .in-addr.arpa
type master;
file "/etc/bind/db.0.168.192"; // В этом файле будут адреса обратной зоны
};
Прямая доменная зона
Ниже просто приведу содержимое файла /etc/bind/db.home.net.
$TTL 3h
@ IN SOA dc.home.net. administrator.home.net. (
; Вместо @ будет подставлено автоматически имя зоны из файла /etc/bind/named.conf.default-zones
2017092701 ; Это число нужно менять каждый раз при изменении этого файла
; Очень удобно взять текущее число и добавить ещё пару цифр для указания ревизии
3h ;
1h ;
1w ;
1h
)
IN NS dc.home.net. ; Указываем имя основного DNS-сервера, обслуживающего зону
; Наличие пробела или табуляции в начале строки и точки
; после .net обязательно
dc IN A 192.168.0.1 ; IP-адрес сервера имен
imap IN CNAME dc ; Указываем, что наш DNS-сервер так же отзывается на imap.home.net
smtp IN CNAME dc ; и smtp.home.net
arm01 IN A 192.168.0.101
arm02 IN A 192.168.0.102
arm03 IN A 192.168.0.103
arm04 IN A 192.168.0.104
arm05 IN A 192.168.0.105
arm06 IN A 192.168.0.106
arm07 IN A 192.168.0.107
arm08 IN A 192.168.0.108
arm09 IN A 192.168.0.109
arm10 IN A 192.168.0.110
Обратная зона
Создадим файл, на который ссылается обратная зона, и заполним его по образцу:
$TTL 3h
@ IN SOA dc.home.net. administrator.home.net. (
2017092701 ; Это число тоже нужно менять
3h ;
1h ;
1w ;
1h
)
IN NS dc.home.net.
1 IN PTR dc.home.net. ; Наш сервер
101 IN PTR arm01.home.net.
102 IN PTR arm02.home.net.
103 IN PTR arm03.home.net.
104 IN PTR arm04.home.net.
105 IN PTR arm05.home.net.
106 IN PTR arm06.home.net.
107 IN PTR arm07.home.net.
108 IN PTR arm08.home.net.
109 IN PTR arm09.home.net.
110 IN PTR arm10.home.net.
Запуск сервера и проверка работоспособности
Если всё сделано правильно, точки расставлены и IP-адреса указаны верно, можно попробовать запустить наш сервер:
service bind9 start
Если вместо кучи ошибок сервер просто написал, что всё хорошо, можно проверить разрешение имен:
host arm01.home.net
host 192.168.0.9
В обоих случаях сервер должен вернуть информацию о том, на что указывает доменное имя или IP-адрес. Если вместо этого он выдает сообщение об ошибке, самое время проверить корректность конфигурационных файлов. Это делается вызовом следующих несложных команд:
named-checkconf /etc/bind/named.conf
named-checkzone home.net /etc/bind/db.home.net
named-checkzone 192.168.0 /etc/bind/db.0.168.192
Данные утилиты из состава пакета bind9utils укажут на ошибки в конфигурационных файлах.
Клиенты
Существует несколько способов настроить обращение к DNS-серверу на клиентских машинах. По скольку речь идет об операционной системе для социальной группы 'siloviki', выбор инструментальных средств невелик - wicd, /etc/network/interfaces и /etc/resolv.conf.
Первый способ отметаем сразу же, поскольку с wicd довольно много проблем. Не буду останавливаться на них подробно, но обычно первое, что приходится делать на свежеустановленной системе - убрать его из автозагрузки:
service wicd stop
chkconfig wicd off
Можно указать DNS-сервер в настройках сетевых интерфейсов, однако, в этом случае их нужно прописывать для каждого сетевого интерфейса, а я не люблю повторяться. В связи с этим просто откорректируем файл /etc/resolv.conf на клиентских машинах:
domain home.net
search home.net
nameserver 192.168.0.1
Работа в ненулевых мандатных режимах
В данном дистрибутиве реализована мандатная модель разграничения доступа, в том числе к сетевым сервисам. Чтобы обеспечить корректную работу демона named в режимах с мандатной меткой, отличной от нулевой, нужно добавить всего одну строчку в файл /etc/parsec/privsock.conf:
/usr/sbin/named