Debian, Dojo, Django, Python

Делюсь опытом в описанных технологиях. Блог в первую очередь выполняет роль памяток для меня самого.

Краткая памятка по выбору квартиры

Введение

Так уж получилось, что я уже 2 с половиной года живу в Москве. Переезд связан с работой, но речь в этой заметке пойдет не об этом. Основная тема - решение жилищного вопроса. Поскольку контора, с которй я связан сейчас контрактом, не вмешивается в личную жизнь сотрудников и их проблемы, просто расскажу о том, как жилищный вопрос решал я.

Ошибка № 1 - Самостоятельный поиск квартиры

Всё началось с того, что искать квартиру я начал слишком поздно и пытался делать это самостоятельно. Я сейчас объясню, почему делать так не стоит.

Во-первых, 99% недвижимости в Москве (уверен, в провинции почти так же) уже находится в базах риэлторов, и выйти на собственника у вас вряд ли получится. У меня получилось примерно на 20-ый звонок, однако, женщина, которая сдавала квартиру, отказалась иметь со мной дело и предпочла молодую семейную пару (искренне надеюсь, что они разнесли ей там всё).

В одном из успешных случаев дозвона до собственника я узнал, что он как бы не против, но всё равно хочет привлечь знакомого риэлтора, "чтобы составить договор и потом не было проблем". Я могу его понять и будучи на его месте, наверное, сделал бы так же.

Вторая проблема в том, что искать квартиру даже через риэлтора нужно сильно заранее, например, за месяц до планируемой даты заселения. Квартиры не всегда освобождаются к нужному моменту, и иногда вашему риэлтору приходится обращаться к другому (возможно, даже из конкурирующей фирмы!), но для вас это значит только одно - платить за услуги обоих риэлторов будете вы. В моём случае так и вышло.

Надо ли говорить о том, что для оплаты нужно готовить сразу стоимость аренды за 2 месяца плюс процент (50 - минимум) риэлтору?

Ошибка № 2 - Выбор квартиры в спешке

К счастью, мой риэлтор - это сын маминой подруги в хорошем смысле. Я приехал на смотр одной квартиры на ВДНХ. Был конец ноября, на улице стоял мороз, а на мне только джинсы и осеннее пальто. Тот человек, что должен был показывать мне квартиру, задерживался. Я прождал его около часа и как следует промёрз. Наконец, он появился и мы пошли в сталинку смотреть жильё.

Старая кровать и старая сантехника, кухня 2 на 2, ржавчина, нерегулируемые батареи. Но там было тепло, что после долгого времени на холоде в не самой тёплой моей одежде очень меня обрадовало.

Вечером я позвонил своему риэлтору и он спросил, что я думаю насчет этой квартиры.

- Ну, мне понравилось, - ответил я.

- А что именно тебе там понравилось? - спросил он.

Тут я немного подзавис, поскольку вопрос оказался очень правильным. Но я не нашёл ничего лучше, чем ответить:

- Там тепло.

- Макс, скажи, адрес квартиры - ВДНХ, %FULL_ADDRESS%?

- Да.

- Ну смотри, человек за нее хочет 30 тысяч. Но это реально грустная квартира. Ты же хочешь, чтобы у тебя подруга к тебе переехала чуть позже?

- Да, хочу.

- Я тебе гарантирую, что в эту квартиру она не поедет никогда в жизни. На эту бабушкину кровать и паркетный пол - ни за что.

- А что же делать? - спросил я.

- Будем искать, но нужно время, - ответил он.

Выводы сделал ещё Омар Хайям:

Чтоб мудро жизнь прожить, знать надобно не мало,
Два важных правила запомни для начала:
Ты лучше голодай, чем что попало есть,
И лучше будь один, чем вместе с кем попало.

Тонкости

Ну а сейчас я хочу поделиться тонкостями, о которых даже не подозревал и нигде не читал.

Дом

Хрущебы - не самый лучший вариант. Лучше поискать новый дом (ну хотя бы не старше 10 лет), пусть даже чуть дальше от метро (зато сильно дешевле).

Время осмотра

Смотреть квартиру лучше всего после 18 часов. Днём люди на работе, а вот вечером они уже обычно дома. Только так можно будет понять, будут ли проблемы с соседями. Если вы пришли смотреть квартиру, а сосед сверху сверлит - будьте уверены, он так делает ВСЕГДА, и завтра это не закончится.

Для примера: в 2012 году я дембельнулся, а родители моей нынешней жены как раз купили ей квартиру в новом доме. Сейчас 2018 год, но наш сосед сверху до сих пор свелит что-то. К сожалению, я не знаю, что именно можно сверлить 6 (!) лет подряд, потому что он предусмотрительно поставил железные двери на выход из лифта и лестничную клетку на своём этаже. Впрочем, они не смогут защищать его всегда, потому что иногда ему приходится выходить на улицу. Уверен, однажды мы встретимся лицом к лицу в крайне ограниченном пространстве, и для одного из нас эта встреча плохо кончится.

Лестничная клетка и двери

На лестничной клетке пивные бутылки, мусор и грязь? Может, лучше поискать другое жильё, чем решать проблемы с соседями, их детьми, гопотой, алкашами? Попробуйте поискать дом, в котором проживают люди.

Железная дверь на входе на лестничную клетку - отличная штука. Ещё одна на входе в квартиру, да ещё и открывающаяся как и положено наружу, а не внутрь - ещё лучше.

Окна

Откройте окна. Слышите улицу? Окна выходят на проезжую часть? От такой квартиры лучше отказаться. Окна должны выходить во двор. А ещё желательно этаж повыше, но не на самой макушке. Примерно 10-ый подойдет, улицу уже не будет так хорошо слышно, как, например, на 6-ом.

Пластиковые окна это хорошо, но они должны уметь открываться на микрощелевое проветривание. К сожалению, в квартире, что я снимаю, окна старого типа.

Вентиляция

Вентиляция. Возьмите с собой зажигалку и проверьте, как она работает. Например, там, где я живу сейчас, она не работает совсем. Бывало, что я просыпался ночью от удушья и шёл на кухню открывать окно (а оттуда шум, потому что оно выходит на заднюю сторону торгового центра, и почти каждую ночь там шумят грузовики, которые привозят товар). Надо ли рассказывать про обмен воздуха в туалете?

Наверно, идеальный вариант - кондиционер, но это здорово повышает стоимость аренды.

Отопление

В 2k18 батареи ОБЯЗАНЫ иметь вентили регулировки. Тёплый пол - лучшее, что может быть.

Коммунальные платежи

Обязательно наличие счётчиков тепла, воды и электричества. Я не видел ни одного договора аренды, чтобы там коммунальные услуги оплачивал хозяин квартиры. Платить за это всё будете вы, а если там ещё и счётчиков нет - то по нормативу, а не по количеству потребленного, а это примерно в 2-3 раза дороже.

Если счётчиков нет, лучше поискать другой вариант, поскольку установка займёт какое-то время и потребует некоторых разходов (перекрытие одного стояка во всём доме - минимум 1000 рублей в час). Возможно, получится договориться с арендодателем и включить их в счет оплаты за очередной месяц, но что, если нет? А деньги за месяц вперед уже заплачены и отступать некуда.

Мебель и бытовая техника

Мебель и бытовая техника повышает стоимость аренды на 20-30%, но нужно спросить себя - а это действительно нужно? Простая математика. Допустим, аренда квартиры с мебелью стоит 30 тысяч рублей, а без неё - 25 (цифры примерные, разница может быть больше или меньше). Разница в год - 12 месяцев умножаем на 5 - 60 тысяч. Как думаете, можно ли за эти деньги купить шкаф, стиральную машину и кровать из IKEA? Я думаю, что да. А телевизор в этой квартире какой? Плазма с диагональю в 3 метра или старый ВЭЛС, который ловит три канала? Вы уверены, что хотите за это платить?

Вся сантехника, проводка и техника должна быть исправна. Зачем вам решать проблемы хозяина? Сдать квартиру - в его интересах, а вы можете поискать что-то другое.

Полы

Пол лучше ламинированный. Паркет скрипит, плохо моется, собирает пыль.

Документы

Все документы должны быть в порядке. Снимите копии с права собственности, паспорта хозяина и так далее. Если он против - это повод насторожиться. Скорее всего, вас хотят кинуть.

Разное

Хозяйские вещи. Желательно, чтобы кроме мебели, если снимаете с ней, ничего не было. Ну разве что газовая плита. У меня на балконе в шкафу ковры. В шкафах в прихожей - компьютер начала 90-х и какие-то штуки для ремонта. Зачем всё это - здесь?

Elpy, python-mode и use-package

Emacs - отличный редактор для тех, кто пишет на Python, и есть куча пакетов для поддержки этого языка. Я уже раньше писал, что теперь храню свою конфигурацию и управляю пакетами с помощью use-package, однако, долго не мог найти способ для красивого скрещивания замечательного пакета elpy cо стандартным python-mode.

Решение оказалось не очень сложным, хотя на его поиск пришлось потратить некоторое время. Ниже выкладываю кусок своего init.el.

init.el для elpy + python-mode
(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
      ;;; 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.

/etc/init.d/bind9

PIDFILE=/var/bind9/chroot/var/run/named/named.pid

Чтобы сервер понимал, что его запускают в изолированном окружении, нужно изменить параметры его запуска, внеся небольшие коррективы в файл /etc/default/bind9

/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:

/etc/bind/named.conf.options

options {
    ...
    version none; // Скроем версию bind
    allow-query {
        192.168.0/24; // Отвечать будем только на запросы, приходящие из внутренней сети
    };
    ...
}

Включение в конфигурацию новой доменной зоны

В файле /etc/bind/named.conf.default-zones добавим указание на файлы прямой и обратной зоны, чтобы сервер мог обслуживать локальную сеть:

/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.

/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

Обратная зона

Создадим файл, на который ссылается обратная зона, и заполним его по образцу:

/etc/bind/db.0.168.192

$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

Управление пакетами в EMACS с помощью use-package

Что такое use-package и как с ним бороться.

Автор данного пакета преследовал цель удобного управления своими пакетами, поддержание чистоты конфигурационных файлов EMACS и по возможности оптимизацию запуска. Ему удалось их успешно решить, а я решил воспользоваться результатами чуждого труда и заодно поделиться информацией. Почитать на англиском про use-package можно здесь, а в статье я лишь оставлю заметку о том, что реально пригодилось.

~/.emacs

Мой файл .emacs содержит довольно мало строк: установщик некоторых базовых пакетов, загрузчик файлов конфигурации, немного комментариев. Выглядит это всё так:

            ;;; package --- Summary
;;; Commentary:
;;; Main EMACS settings file, load settings from parts.

;;; Code:

(require 'package)

;;; Источники для установки пакетов. Дело в том, что MELPA Stable
;;; содержит не очень свежие версии, поэтому иногда лучше ставить
;;; пакеты из основного репозитория.

(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/"))
(add-to-list 'package-archives '("melpa"        . "http://melpa.org/packages/"))
(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)
(setq use-package-always-ensure t)

;;; Указываем откуда брать части настроек.
(defconst user-init-dir
  (cond ((boundp 'user-emacs-directory) user-emacs-directory)
        ((boundp 'user-init-directory) user-init-directory)
        (t "~/.emacs.d/")))

;;; Функция для загрузки настроек из указанного файла.
(defun load-user-file (file)
  (interactive "f")
  "Load a file in current user's configuration directory"
  (load-file (expand-file-name file user-init-dir)))

;;; Части конфигурации. Порядок не имеет принципиального значения,
;;; однако я рекомендую некоторые базовые вещи помещать в начало,
;;; чтобы не было необходимости вспоминать базовые команды EMACS
;;; если в результате улучшения сломается один из базовых конфигов.
(load-user-file "neotree.el")
(load-user-file "personal.el")
(load-user-file "themes.el")
(load-user-file "flycheck.el")
(load-user-file "company.el")
(load-user-file "ibuffer.el")
(load-user-file "python.el")
(load-user-file "web.el")
(load-user-file "javascript.el")
(load-user-file "markdown.el")
(load-user-file "json.el")
(load-user-file "yasnippet.el")
(load-user-file "helm.el")
(load-user-file "keyboard.el")
(load-user-file "fonts.el")
(load-user-file "powerline.el")
(load-user-file "elisp.el")

;;; А здесь EMACS хранит настройки, задаваемые через customize
(setq custom-file "~/.emacs.d/customize.el")
(load-user-file "customize.el")

;;; .emacs ends here
        

Половина дела сделана. Настройки размещены в разных файлах, каждый из которых не зависит от других. Вот теперь заживём!

neotree.el

            ;;; Package --- Summary
;;; Commentary:
;;; NeoTree - sidebar for file browsing

;;; Code:

;;; Как я и говорил, пакет будет скачан и установлен автоматически.
(use-package
  neotree
  :bind ([f8] . neotree-toggle)      ;;; Так делается привязка клавиш.
  :init (setq neo-window-width 35)   ;;; Настройки ДО загрузки пакета.
  :config (setq neo-smart-open nil)) ;;; Настройки ПОСЛЕ загрузки пакета.

;;; neotree.el ends here
        

Полагаю, данный пример наглядно показывает, как пользоваться use-package. Продолжим.

personal.el

            ;;; Package --- Summary
;;; Commentary:
;;; User settings for .emacs

;;; Code:

;;; Закрывать *scratch* при запуске.
(kill-buffer "*scratch*")

;;; Цветные скобочки
(use-package
  rainbow-delimiters
  :init (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
        (setq rainbow-delimiters-max-face-count 9))

;;; Scrolling
(setq scroll-step               1) ;; one line
(setq scroll-margin            10) ;; scroll buffer to 10 lines at going to last line
(setq scroll-conservatively 10000)
(setq directory-free-space-args "-Pm")

;; Подсветка результатов поиска и всё такое
(setq search-highlight        t)
(setq query-replace-highlight t)

(setq column-number-mode 1) ;; Номера строк слева
(setq use-dialog-box nil)   ;; Не нужны нам диалоги, будем всё руками делать
(auto-fill-mode -1)         ;; Не знаю, что за параметр, так и не разобрался
(setq-default tab-width          4) ;; Заменить TAB на 4 пробела
(setq-default standart-indent    4) ;; Стандартный отступ - 4 пробела
(setq backup-inhibited t)           ;; Backup'ы тоже делать не будем
(setq auto-save-default nil)        ;; Автосохранение не нужно
(setq scroll-preserve-screen-position 10) ;; Показывать не менее 10 строк на экране

(setq-default c-basic-offset 4 c-indent-level 4 indent-tabs-mode nil) ;; TAB'ы не нужны

(setq-default save-place t) ;; Помнить, где был курсор в прошлый раз

;;; Нажатие Insert больше не включает режим замены
(define-key global-map [(insert)] nil)

;;; Автоформатирование перед сохранением
(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)

(cua-mode 1)          ;;; Ctrl+C, Ctrl+V! Прямо как в Windows!
(desktop-save-mode 1) ;;; Помнить, какие файлы были открыты в прошлый раз
(fset 'yes-or-no-p 'y-or-n-p) ;;; Вместо yes и no понимать y и n
(global-hl-line-mode 1) ;;; Подсветка текущей строки
(global-linum-mode 1)   ;;; Показывать номера строк всегда
(menu-bar-mode -1)      ;;; А меню - никогда
(scroll-bar-mode -1)    ;;; Скроллбар не нужен
(tool-bar-mode -1)      ;;; Тулбар не нужен

;;; Умные скобочки
(use-package
  smartparens
  :config (smartparens-global-mode 1))

;; Electric pair mode
(electric-pair-mode 1)

;; Иконки в статус-баре
(use-package
  mode-icons
  :config (mode-icons-mode 1))

;; Показывать отступы во всех режимах
(use-package
  indent-guide
  :config (indent-guide-global-mode 1))

;;; Дерево отмены
(use-package
  undo-tree
  :config (global-undo-tree-mode 1))

;;; personal.el ends here
        

python.el

Очень интересный конфиг получается для Python.

            ;;; Package --- Summary
;;; Commentary:
;;; Settings for Python

;;; Code:

(defun annotate-pdb()
  (interactive)
  (highlight-lines-matching-regexp "import ipdb")
  (highlight-lines-matching-regexp "import pdb")
  (highlight-lines-matching-regexp "set_trace()")
  (highlight-regexp "^TODO ")
  (highlight-phrase "FIXME"))

(use-package
  python
  :mode ("\\.py'" . python-mode)
  :init (progn
          (defalias 'python2-mode 'python-mode)
          (defalias 'python3-mode 'python-mod))
  :config (setq-default py-separator-char 47) ;; Use spaces instead tab
  (setq-default python-indent-offset 4) ;; 4 spaces instead 2 for python-mode
  )

(use-package
  company-anaconda
  :ensure t
  :init (add-to-list 'company-backends 'company-anaconda))

(use-package
  py-autopep8
  :init (progn (add-hook 'python-mode-hook 'py-autopep8-enable-on-save)))

(use-package
  anaconda-mode
  :commands anaconda-mode
  :diminish anaconda-mode
  :init (progn (add-hook 'python-mode-hook 'anaconda-mode)
               (add-hook 'python-mode-hook 'eldoc-mode)
               (add-hook 'python-mode-hook 'annotate-pdb)))

(use-package
  pyvenv
  :config (defalias 'workon 'pyvenv-workon))

(use-package
  py-isort
  :config (setq py-isort-options '("-sl"))
  :init (progn (add-hook 'python-mode-hook 'py-isort-before-save)))

(use-package
  company-anaconda
  :config (add-to-list 'company-backends 'company-anaconda))

;;; python.el ends here 
        

Не уверен, что нужно что-то тут объяснять, но на всякий случай свой company.el тоже выставляю напоказ.

company.el

Этот модуль отвечает за подсказки и автозавершение. Он новее, чем autocomplete, и активно развивается. Большая часть плагинов дополнения сейчас пишется именно под него.

            ;;; Package --- Summary
;;; Commentary:
;;; Settings for company

;;; Code:

(use-package
  company
  :diminish company-mode
  :config (setq company-backends (remove 'company-ropemacs company-backends) company-tooltip-limit
                20 company-tooltip-align-annotations t)
  (global-company-mode 1))

;;; company.el ends here
        

Gulp для сборки приожений Angular 1.x

Немного про Gulp

Было время, когда мне очень нравился Grunt, а причина была проста - другие средства сборки Web-проектов только начинали развиваться (2013 год, если что). Вскоре я открыл для себя Gulp. В сравнении с Grunt это был действительно удобный инструмент, и я надолго остановился именно на нём.

Всё никак не найду время на изучение Angular 2 и Webpack, поэтому один из мелких проектов пишу на Angular 1.x (актуальная версия на момент написания статьи - 1.6.x). Кто писал на нём, знает, что все контроллеры, директивы, сервисы и прочие компоненты лучше размещать в отдельных файлах, однако, потом на странице нужно подключать каждый файл отдельно.

Мириться с этим было нельзя, я поискал на StackOverflow похожие запросы и выяснил, что многие сталкивались с этой проблемой. Я предлагаю своё решение, являющееся компиляцией из разных источников.

Установка пакетов

В каталоге проекта инициализируем npm, отвечаем на несколько простых вопросов, после чего ставим необходимые плагины:

npm init && npm install gulp gulp-rename gulp-uglify gulp-include gulp-watch --save
Название Описание
gulp-rename Позволяет указать имя целевого файла, отличное от исходного. Используется для создания отладочных и минимизированных версий файлов.
gulp-uglify Сжимает JS. Используется для создания минимизированных продуктовых версий.
gulp-include Позволяет вставлять код из одного файла в другой подобно тому, как это делает директива #include> в C-подобных языках. Так же позволяет делать вызов require, при этом код будет вставлен только в том случае, если не вставлялся ранее.
gulp-watch Отслеживает изменения в нужных файлах, при необходимости автоматически вызывая нужные Gulp Task

gulpfile.js

В каталоге приложения так же создадим файл по имени gulpfile.js (регистр важен) со следующим содержимым:

gulpfile.js
(function (r) {
    "use strict";

    var gulp = r("gulp"),
        uglify = r("gulp-uglify"),
        include = r("gulp-include"),
        rename = r("gulp-rename"),
        sources = {
            login: "./src/login.js",
            app: "./src/app.js"
        },
        DEST = "./todoist/static/app/"

    gulp.task("js-dev", function () {
        // Сборка без минификации, специально для отладки
        gulp.src(sources.login)
            .pipe(gulp.dest(DEST));

        gulp.src(sources.app)
            .pipe(include())
            .pipe(gulp.dest(DEST));
    });

    gulp.task("js-prod", function () {
        gulp.src(sources.login)
            .pipe(uglify())
            .pipe(rename("login.min.js"))
            .pipe(gulp.dest("./todoist/static/app/login.min.js"));

        gulp.src(sources.app)
            .pipe(include())
            .pipe(uglify())
            .pipe(rename("app.min.js"))
            .pipe(gulp.dest(DEST));
    });

    gulp.task("watch", function () {
        gulp.watch("./src/**/*.js", ["js-dev"]);
    });

    gulp.task("default", ["js-dev", "watch"]);

}(require));

Надеюсь, комментарии к тому, что тут происходит, не нужны.

Файлы приложения

Все исходные файлы приложения у меня лежат в src/, для контроллеров создан каталог controllers, для сервисов - services (директивы и фильтры пока не успел написать, поэтому и каталогов нет).

Так выглядит главный файл приложения:

app.js
(function (A) {
    "use strict";
    var app = A.module("todoist", ["ui.bootstrap", "ngRoute", "ngCookies"]);

    app.config(["$interpolateProvider", "$httpProvider", "$routeProvider", function ($interpolateProvider, $httpProvider, $routeProvider) {
        $interpolateProvider.startSymbol("{$");
        $interpolateProvider.endSymbol("$}");

        $httpProvider.defaults.xsrfCookieName = "csrftoken";
        $httpProvider.defaults.xsrfHeaderName = "X-CSRFToken";
        $httpProvider.defaults.headers.common.Authorization = "Basic";


        $routeProvider.when("/", {
            controller: "MainController",
            templateUrl: "/ui/index.html"
        }).when("/users", {
            controller: "UserListController",
            templateUrl: "/ui/users/list.html"
        }).when("/users/:id", {
            controller: "UserDetailController",
            templateUrl: "/ui/users/detail.html"
        }).otherwise({
            redirectTo: "/login"
        })
    }]);

    //=include ./controllers/*.js
    //=include ./services/*.js"
}(this.angular));

При сборке вместо комментариев //=include ./controllers/*.js будет вставлено содержимое лежащих там файлов - как раз то, чего я и хотел добиться.

Последний шаг - подключить файл приложения в шаблоне Django:

<script src="{% static 'app/app.js' %}"></script>

Настройка сети в Astra Linux

Все выполняемые операции требуют привилегий пользователя root.

Astra Linux использует для конфигурирования сетей собственную утилиту wicd. В общем, она довольно удобна, но у неё есть фатальный недостаток - её писали не мы сеть не будет работать до авторизации пользователя в системе. Для обычных компьютеров в этом нет ничего страшного, однако, для сервера это большая проблема, так как иногда его приходится перезагружать по SSH.

Пусть компьютеры будут находиться в сети с адресами 192.168.0.XXX, где вместо XXX - число от 1 до 254.

Настройка осуществляется путем правки файла /etc/network/interfaces. Каждый сетевой интерфейс (сетевая карта, хотя это не совсем точное название) настраивается отдельно. Настройки для сервера выглядят так:

/etc/network/interfaces
auto lo eth0

iface lo inet loopback

iface eth0 inet static
    address 192.168.0.1
    netmask 255.255.255.0
    gateway 192.168.0.1     # В качестве шлюза - наш сервер с IP=1
    network 192.168.0.0     # Указываем сеть, это обязательно для работы в составе ALD
    broadcast 192.168.0.255 # Сервер ALD начиная с Astra 1.5 выводит ошибку, если не видит
                            # этого параметра в настройках сети.
    dns-nameservers 192.168.0.1

Первая строчка auto lo eth0 указывает, какие интерфейсы должны быть запущены при загрузке ОС. Отмечу, что локальная петля lo должна присутствовать там в любом случае.

Пропустим описание локальной петли и сразу перейдем к сетевому интерфейсу.

iface Ключевое слово, говорящее о том, что дальше будет описание сетевого интерфейса
eth0 Указываем, что данный сетевой интерфейс должен быть привязан к сетевой карте eth0. Посмотреть список карт можно командой: lshw -class network
inet Указываем, что это будет настройка сети.
static При этом все настройки будут указаны вручную.
address IPv4-адрес компьютера
netmask Маска подсети.
gateway Шлюз, т. е. IP-адрес, через который идёт подключение к интернету. Обычно на сервере указывают адрес, выданный провайдером, но в нашем случае (закрытый от мира сегмент) пусть будет 192.168.150.1, т. е. компьютер обращается сам к себе.
dns-nameservers Список разделенных пробелами IP-адресов DNS-серверов. Полезно при разворачивании ЕПП под управлением Astra Linux и настройке приложения bind.

На клиентских компьютерах настройки следует выполнить аналогичным образом, меняя только четвёртый октет в поле address.

На этом настройка не заканчивается. Теперь нужно отключить автозапуск встроенных утилит и остановить уже запущенный экземпляр службы wicd, после чего перезапустить службу поддержки сети.

service wicd stop
chkconfig wicd off
rm /etc/xdg/autostart/fly-admin-wicd.desktop
service networking restart