вторник, 25 ноября 2014 г.

Создание своей репы с poudriere(8)

Небольшая шпаргалка по созданию своей репы с помощью ports-mgmt/poudriere.

Для полного понимания происходящего нужно уметь элементарно работать с портами: знать как выставлять опции, ставить/удалять порт, знать про /etc/make.conf и понимать в каком формате туда можно дописывать опции.

При написаниии использовал эти ссылки:
http://blather.michaelwlucas.com/archives/1941
http://www.bsdnow.tv/tutorials/poudriere
http://forums.freebsd.org/viewtopic.php?t=38859

Введение

Как известно, штатным источником софта во FreeBSD является дерево портов. Когда вы ставите софт из портов, система собирает на самом деле из порта пакет, и пакет уже устанавливается в систему. Для работы с пакетами используется утилита pkg(8).
Соответственно, вместо компилирования пакетов из портов на каждой подконтрольной машине, удобней иметь некий общий для всех машин репозиторий, с уже собранными пакетами.
pkg(8) как пакетный менеджер умеет работать с удаленными репозиториями, т. е. ставить софт из них, разрешать зависимости и т. д. Стандартной утилитой для создания своего репозитория сейчас считается poudriere(8), которая используется в том числе для сборки официальной фряшной репы.
В этой статье я создам свой репозиторий с помощью poudriere(8) с нуля, настрою http-сервер для отдачи пакетов, а затем настрою pkg(8) для того, чтобы он брал софт из этой репы.
При этом я в своей работе обычно использую только свою репу и добавляю туда софт или изменяю опции по надобности. Официальную репу FreeBSD я обычно использую только на самом раннем старте создания репы, а потом дизаблю.

Чуть подробнее

Poudriere(8) после сборки просто кладёт в указанную вами папку все пакеты и файлы метаданных. Т. е. он только собирает, готовит репу и ничего больше. Какой протокол вы будете использовать для работы с репозиторием это уже ваше дело. pkg(8) умеет работать с HTTP и FTP, например. Полный список протоколов в ман-странице. Вы берете любой http или ftp сервер и настраиваете его указывая, что вот тут лежат файлы, которые ты должен отдавать.
Я же в качестве транспорта буду использовать HTTP. Еще poudriere (8) умеет подписывать собранные пакеты , но я не буду использовать подпись, так проще.

Настройка poudriere(8)

У меня все данные, занимающие много места хранятся в каталоге /storage. В нём я создаю отдельный каталог poudriere, который будет являться рабочим каталогом для утилиты poudriere(8), т. е. там будет проходить сборка, храниться мир и дерево портов для сборки, логи сборки, сами пакеты с метаданными и т. д.
Создаю каталоги, сразу для всего
# mkdir -p /storage/poudriere/distfiles
По названию папки distfiles понятно зачем она нужна.

Затем устанавливаю минимальный набор софта, нужный для разворачивания и конфигурирования репы. Пока из официальной репы, т. к. других источником нет, да и разницы нет никакой.
Бутстрапим пакетный менеджер:
# pkg
Ставим poudriere(8):
# pkg install ports-mgmt/poudriere
Ставим утилиту для работы с фреймворком опций портов, т. е. чтобы можно было выбирать опции при сборке порта:
# pkg install ports-mgmt/dialog4ports

Далее редактирую конфиг для poudriere(8), скопировав sample. Тут сам конфиг хорошо откомментирован. В общем, всё можно найти в документации. У меня на сервере сборки используется только UFS.
# egrep -v '^\s*(#|$)' /usr/local/etc/poudriere.conf
NO_ZFS=yes
FREEBSD_HOST=ftp://ftp.freebsd.org
RESOLV_CONF=/etc/resolv.conf
BASEFS=/storage/poudriere
USE_PORTLINT=no
USE_TMPFS=yes
DISTFILES_CACHE=/storage/poudriere/distfiles
CHECK_CHANGED_OPTIONS=verbose #
CHECK_CHANGED_DEPS=yes #Ask when options on changing
NOLINUX=yes

Далее создаю каталог, где будут храниться вспомогательные конфиги, если он еще не создан:
# mkdir /usr/local/etc/poudriere.d

Вообще с помощью poudriere можно создавать любое число веток портов для любого количества релизов FreeBSD. Устроено это следующим образом.
С poudriere(8) вы создаёте окружение для сборки, иммитирующее нужный вам релиз. Такое окружение это "мир" нужного релиза, распакованный в определенную директорию. Вся эта штука в терминологии poudriere(8) называется jail. Тут есть некоторая путаница из-за такого названия. Это _не_ тот самый типичными фряшными джэйл, нет. Это что-то вроде контейнера. Хотя в процессе сборки poudriere(8) будет автоматом без вашего участия динамически создавать, а затем уничтожать пачку именно фряшных джэлов. Всё, чем нужно рулить вам, как пользователю - это poudriere jail'ы. Poudriere jail имеет такие параметры как имя, архитектура и релиз, для которого будет скачан и распакован "мир".
Таким образом достигается подход, когда на одной машине вы можете собирать пакеты для разных релизов и архитектур, просто создавая пачку таких poudriere jail'ов.

Создание окружения

Например, чтобы создать poudriere jail для 10.2-RELEASE архитектуры amd64 и с именем FreeBSD:10:amd64 нужно выполнить:
# poudriere jail -c -j FreeBSD:10:amd64 -v 10.2-RELEASE -a amd64
После этого poudriere(8) сам всё скачает и распакует в /storage/poudriere/jails.
В данном случае я выбрал имя "FreeBSD:10:amd64" для poudriere jail'а для удобства дальнейшего использования, т. к. такое имя полностью соответствует переменной ${ABI}, определяющей архитектуру релиза в некоторых утилитах. Так мне проще будет настраивать потом pkg(8).
Таком же образом можно создать остальные poudriere jail'ы, соответствующий другим релизам.
Я использую только последний релиз и собираю пакеты только для него. Стоит также помнить, что между релизами одной мажорной ветки обеспечена бинарная совместимость. Вывести список poudriere jail можно командой:
# poudriere jail -l
Поскольку poudriere jail это обычный "мир", то для него справедливы все те выходящие патчи от FreeBSD, закрывающие разные дыры. Подтянуть все фиксы, т. е. обновить poudriere jail, как бы это делалось для обычного сервера командой freebsd-update fetch/install, можно так:
# poudriere jail -u -j FreeBSD:10:amd64
Рекомендую это делать с такой же периодичностью, как для настроящего сервера с таким же релизом.

Дерево портов

Далее нам нужно дерево портов, из которого собственно будут собираться наши пакеты. Можно использовать уже существующее дерево портов, если оно у вас уже есть. Однако, я предпочитаю использовать отдельное дерево портов для нужд poudriere. Во-первых, я ставлю серверы без дерева портов, т. е. у меня нет его в привычном /usr/ports, т. к. это просто не нужно. Я ведь пакетами буду пользоваться. Во-вторых, мне кажется, что так больше порядка.
В poudriere(8) есть возможность создавать сколько угодно разных веток портов с привязкой к своему poudriere jail'у. Я с такими вещами не заморачиваюсь. Я использую только 1 ветку портов для всего. Мне это не нужно, да и так намного проще. Я делаю так:
# poudriere ports -c
Если было бы нужно с привязкой к конкретному джэйлу, то:
# poudriere ports -c -p JailName
После этого всё само скачается и распакуется в /storage/poudriere/ports/(default|JailName).
Если бы нужно было использовать уже существующее дерево портов в привычном /usr/ports, то я бы ввел:
# poudriere ports -c -F -f none -M /usr/ports -p default 
# poudriere ports -c -f none -m null -M /usr/ports -p default
Обновлять бы такое дерево можно было бы с помощью штатной утилиты portsnap(8) (или svn, что привычнее), как и раньше.
В ином, т. е. моем случае нужно просить об этом poudriere(8):
# poudriere ports -u
Если у нас не одно дерево портов, то посмотреть список можно так:
# poudriere ports -l
Теперь нужно определить список портов для сборки. Я задал свой список портов для сборки таким образом:
# cat /usr/local/etc/poudriere.d/pkglist.txt
...
www/apache24
lang/php56
net/freeradius2
net/openldap24-server
...

Опции портов

Обычно при работе с деревом портов можно использовать файл /etc/make.conf для задания предопределенных опций по умолчанию. Например, сразу отключить такие опции как иксы или дебаг. С poudriere(8) тоже можно использовать подход с опциями в make.conf, только это файл будет располагаться в другом месте. Поскольку у меня только одно дерево портов, мой make.conf будет располагаться тут:
# cat /usr/local/etc/poudriere.d/make.conf
OPTIONS_UNSET=X11 HAL DEBUG EGL
WANT_OPENLDAP_SASL="YES"
#SQUID_CONFIGURE_ARGS="--enable-useragent-log"
В файле у меня уже определено некоторое количество опций. Синтаксис меняется со временем, нужно сверяться с текущим фреймворком для задания опций.
Если бы у меня было несколько деревьев портов для каждого poudriere jail, то имя файла было бы иным:
# cat /usr/local/etc/poudriere.d/JailName-make.conf
Полностью опции для портов задаются опять же с помощью вызова poudriere(8). Т. е. никакие `make config` и т. д. тут не работают. Мало того, все определенные poudriere(8) опции хранятся в другом месте, отличном от /var/db/ports. А точнее в папке /usr/local/etc/poudriere.d/options. Поскольку я использую одно дерево портов для всего, я не могу сказать как будет в случае нескольких веток для разных poudriere jail.
Итак, задать опции для всех портов, перечисленных в /usr/local/etc/poudriere.d/pkglist.txt можно командой:
# poudriere options -f /usr/local/etc/poudriere.d/pkglist.txt
Т. е. тут poudriere(8) берет список портов из файла и последовательно выводит диалог с опциями. Чтобы задать опции для какого-то конкретного порта, вместо указания опции '-f' и имени файла нужно просто ввести неполный путь к порту, например:
# poudriere options shells/bash
Замечу, что в предыдущих двух случаях опции будут запрашиваться только если они никогда раньше не были заданы или с последнего момента задания опций появились новые. Чтобы всегда выводить диалог с опциями нужно использовать ключ '-c'. Например:
# poudriere options -cf /usr/local/etc/poudriere.d/pkglist.txt
# poudriere options -c shells/bash

Сборка портов

Всё готово. У нас есть окружение, дерево портов, для которых заданы опции. Можно собирать.
Запускаем сборку портов из списка pkglist.txt:
# poudriere bulk -j FreeBSD:10:amd64 -f /usr/local/etc/poudriere.d/pkglist.txt
Чтобы собрать вообще всё дерево портов нужно ввести ключ '-a', но я обычно так не делаю:
# poudriere bulk -j JailName -a
Далее будет долгий процесс сборки, в процессе которого на экран будет выводить куча информации. В процессе сборки можно нажать ctrl+t я посмотреть текущий отчет о том, сколько портов собрано, сколько времени затрачено на сбоку, сколько осталось собрать и т. д.
По окончанию сборки на экране появится отчет.
Мы получили готовый репозитарий. Осталось настроить транспорт и сам pkg(8).

Транспорт и настройка pkg(8)

Я буду раздавать пакеты по HTTP с помощью apache. Можно взять абсолютно любой другой веб-сервер или FTP сервер. Мой конфиг выглядит так:
# cat /usr/local/etc/apache24/Includes/pkg.example.org.conf
<VirtualHost *:80>
    ServerAdmin admin@example.org
    DocumentRoot /storage/poudriere/data/packages
    ServerName pkg.example.org
    <Directory />
        Options Indexes FollowSymLinks
        AllowOverride AuthConfig
        Require all granted
    </Directory>
</VirtualHost>
Мой репозиторий доступен по DNS имени pkg.example.org. Apache можно поставить из офф. репы или из своей же:
# pkg install /storage/poudriere/data/packages/FreeBSD:10:amd64-default/All/apache24-2.4.17.txz
Для pkg(8) всё просто. На всех клиентских машинах я дизаблю официальный репозиторий FreeBSD и использую свой. Т. е. в файле /etc/pkg/FreeBSD.conf меняю опцию 'enabled' с 'yes' на 'no', а затем создаю конфиг:
# mkdir -p /usr/local/etc/pkg/repos
# cat /usr/local/etc/pkg/repos/myrepo.conf
myrepo: {
 url: "http://pkg.example.org/${ABI}-default",
 mirror_type: "none",
 enabled: yes
}
Тут мне пригодилось имя моего poudriere jail, потому что оно соответствует переменной ${ABI}. Так по-моему удобней.
Далее все просто:
# pkg update
# pkg search squid
# pkg install www/squid
# pkg upgrade
и т. д.

Дальнейшее обслуживание

В дальнейшем я просто обновляю дерево портов и запускаю сборку. Будет собраны только те порты, новые версии которых появились в дереве и зависимые от них порты.
# poudriere ports -u
# poudriere bulk -j FreeBSD:10:amd64 -f /usr/local/etc/poudriere.d/pkglist.txt
Если у порта появились новые опции, то poudriere(8) в процессе сборки никаких диалогов не будет выводить. Так что я считаю полезным изредка запускать:
# poudriere options -f /usr/local/etc/poudriere.d/pkglist.txt
Если мы поменяли какие-то опции, то poudrire(8) в процессе сборки выведет список старых и новых опций и пересоберет порт, даже если он остался на той же версии.
Чтобы пересобрать какой-то порт принудительно, например, www/squid, нужно использовать ключ '-c':
# poudriere bulk -c -j FreeBSD:10:amd64 www/squid
Или '-f /usr/local/etc/poudriere.d/pkglist.txt' вместо имени порта, если нужно пересобрать вообще все из списка.
Для удобства я создал такие однобуквенные alias'ы для командного интерпретатора:
# alias | grep poudriere
B       (poudriere bulk -j FreeBSD:10:amd64 -f /usr/local/etc/poudriere.d/pkglist.txt)
P       (poudriere ports -u)
T       (poudriere testport -j FreeBSD:10:amd64 -n -o)
U       (poudriere jail -j FreeBSD:10:amd64 -u)
Вот так и живём.

понедельник, 17 ноября 2014 г.

Немного growfs

Маааленькая шпаргалка.
Понадобилось тут на виртуальной машине увеличить диск.
Увеличил диск на гипервизоре, при загрузке выскочило такое:
GEOM: da1: the secondary GPT header is not in the last LBA.

После этого сделал
# gpart recover da1
# gpart resize -i 1 da1
# growfs /dev/da1p1
Всё.

пятница, 20 июня 2014 г.

Squid и Domain Users

Достаточно много времени прошло с момента ввода в эксплуатацию прокси сервер со squid на борту, с доменном авторизацией. Все было хорошо, все работало.
Однако, в один момент админ одного из филиалов захотел, чтобы пользователей его поддомена наш squid не пускал в интернет. Не долго думая, в группу запрета интернета DenyInternet была добавлена группа Domain Users соответствующего поддомена. Быстро выяснилось, что такая конфигурация не работает. Пришлось разбираться почему.
Оказалось, что в MS AD группа Domain Users это primary группа пользователя, которая не числится в атрибуте memberOf учетки пользователя. Есть отдельный атрибут под названием primaryGroupID, в котором записан идентификатор группы. Именно идентификатор в виде цифры, а не текст или что-то еще. Как правило, если в атрибуте primaryGroupID указано 513, то это Domain Users, 512 - Domain Admins. Но это как правило, т.е. по идее не исключены ситуации, когда это не так.
Чтобы точно знать какой же группе принадлежит этот идентификатор, надо сделать поиск в AD. Что искать? Гугл подсказал, что группу надо искать по атрибуту objectSID. Чтобы его сформировать, надо взять objectSID запрашиваемого пользователя, например S-1-5-21-1232852338-251567878-923712399-72399, отрубить там последние циферки, идущие за последним дефисом. Так мы получить SID домена запрашиваемого пользователя. Вместо тех циферок подставить значение из атрибута primaryGroupID запрашиваемого пользователя. Так мы получим идентификатор группы S-1-5-21-1232852338-251567878-923712399-513, по которому и будет искать в AD. Т.е. теперь нужно выполнить поиск по ldap по атрибуту objectSID=S-1-5-21-1232852338-251567878-923712399-513 и получить имя группы. Поскольку эта группа тоже может иметь атрибут memberOf, в котором записано членство в других группах, то надо рекурсивно пройтись по ним так же, как это делалось в обычными группами.
По началу я попытался сам написать патч. Конечно, знаний у меня маловато, и я не сильно надеялся на успех. В добавок оказалось, что objectSID возвращается в бинарном виде. Тут я совсем сдался и решил написать разработчику хелпера, которого зовут Markus Moeller. В письме изложил ему проблему и идею ее решения. Markus ответил довольно быстро и легко согласился дописать хелпер. В общем диалог у нас вышел легкий. Через несколько дней он прислал патч, который я попробовал и отписался о результатах, потом еще несколько и в итоге все заработало как надо. Markus обещал, что этот патч он включит в транк. Так что в след. версии squid этот функционал будет доступен.
А пока новая версия еще не вышла, можно использовать этот патч для актуальной версии squid.
Писалось все это для squid34, так что к нему и надо применять. Для младших версии нужна правка.
Тут лежит новый файл support_ldap.cc, который надо заменять.
Вот такой хороший человек оказался Markus Moeller, легкий на подъем и в общении. Большое спасибо ему!

UPD. Squid 3.5 уже включает в себя данные изменения.

вторник, 15 апреля 2014 г.

Установка FreeBSD на ufs с шифрованием geom_eli на корневом разделе

Маленькая шпора для меня о том, как поставить Фрю на корневой раздел ufs и шифрованием.
Фокус в том, что /boot должен лежать на отдельном нешифрованном разделе. В скобках необязательные параметры. Диск у меня ada0, и, допустим, он 10 гигов.

Загружаюсь с диска/флэшки. Жму Install, там выбираю keymap, ввожу hostname, выбираю что ставить. А в окне Partitioning жму Shell.

# gpart destroy -F ada0
ada0 destroyed

# gpart create -s gpt ada0
ada0 created

# gpart add -t freebsd-boot (-l bootcode) -s 64k ada0
ada0p1 added

# gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 ada0
bootcode written to ada0

# gpart add -t freebsd-ufs (-l bootfs) -s 1g  ada0
ada0p2 added
Это как раз раздел под /boot. Размера в 1 гиг должно хватить.

# gpart add -t freebsd-ufs (-l rootfs) -s 9g ada0
ada0p3 added
корневой раздел 9 гигов
(# glabel label rootfs /dev/ada0p3) - такую метку можно приделать вообще всем разделам.

# gpart add -t freebsd-swap ada0
ada0p4 added
остаток в swap

Делаю корневой раздел шифрованным:
# geli init -b (-s4096) -l256 (-K keyfile) /dev/ada0p3
Enter new passphrase:
.....
Тут путь к корневому разделу можно заменить любым из label. Также тут можно дополнительно использовать файл-ключ, про генерацию которого написано в мане geli. В таком случае надо будет дописывать доп. строки в /boot/loader.conf, что тоже описано в мане. Тут я использую просто пароль.

Присоединяю шифрованный корневой раздел:
# geli attach /dev/ada0p3
Enter passphrase:...
Должен появиться /dev/ada0p3.eli, с которым уже и надо работать на уровне системы.

Теперь надо создать файловые системы на разделах
# newfs -U -j /dev/ada0p2
# newfs -U -j /dev/ada0p3.eli

Монтирую корень, чтобы установка дальше сама прошла.
# mount /dev/ada0p3.eli /mnt
# exit

Дальше идут этапы установки с вопросами. Когда закончатся все вопросы, а окне Final Configuration выбрать Exit, потом в окне Manual Configuration выбрать No, затем в окне Complete выбрать LiveCD.

Войти под рутом. Тут есть хитрость. На выделенном разделе должна лежать именно директория boot с содержимым, а не просто ее содержимое на верхнем уровне раздела.
# mkdir /mnt/bootext
# mount /dev/ada0p2 /mnt/bootext
# cd /mnt
# mv boot bootext/
# ln -s bootext/boot boot

Теперь осталось настроить параметры для загрузки и монтирования
# vi bootext/boot/loader.conf
geom_eli_load="YES"
vfs.root.mountfrom="ufs:/dev/ada0p3.eli"

# vi etc/fstab
# Device        Mountpoint      FStype  Options Dump    Pass#
/dev/ada0p2    /bootext               ufs     rw      1       1
/dev/ada0p3.eli /                      ufs      rw      1      1
/dev/ada0p4    none            swap    sw      0       0

Все, закончил. Теперь просто перезагрузка
# shutdown -r now

При загрузке спросит пароль. Можно еще swap шифровать, но я не стал.



пятница, 4 апреля 2014 г.

Попробуем 3proxy?

У нас 3proxy уже использовался, но только как socks proxy. Захотелось попробовать его как http прокси. Этот сервер называют еще ЗАРАЗА прокси, умеет и http, и socks и много чего еще. Даже опции перенаправления есть, чтобы составлять цепочки из проксей. Не умеет, правда, кэширования. Штатное перенаправление 3proxy дерективой parent работает отлично. Со squid с доменной аутентификацией не заработал.
Было интересно сдюжит ли 3proxy http нагрузку, да и вообще проверить на наличие подводных камней или ошибок конфигурации. Приведу окончательный полный конфиг в конце, а сейчас скажу, что начальный конфиг не сильно отличался от дефолтного, поправил только списки доступа и другую ерудну. С раннего утра выключил основной squid и запустил на его место 3proxy. По-началу все было хорошо и быстро. Однако, чем дальше, тем хуже становилось. По мере прихода сотрудников на работу периодически все больше веб-страниц переставало открываться или подгружаться полностью. В логе /var/log/messages мелькало
<date> <hostname> kernel: sonewconn: pcb 0xfffff8003012a310: Listen queue overflow: <N> already in queue awaiting acceptance
После быстрого гугления я узнал о listen queue (и еще многих других ограничениях системы, в которые можно упереться) и как их посмотреть. (хорошие ссыль1 и ссыль2)
# netstat -Lan
сказал мне, что у меня для 3proxy на порту 3128 в очереди больше соединений на подключение, чем их может быть максимум. Увеличив максимиум на уровне системы до 512, ситуация не изменилась:
kern.ipc.somaxconn=512
Вообще в это время в логах 3proxy светилось бы такое:
Warning: too many connected clients (N/M)
Но из-за того, что у меня в настройках 3proxy был выставлен формат логов совместимый со squid (для анализаторов), там был бы только ошметок от этой фразы, и конечно в общем потоке заметить это было сложно. Но ко всему прочему я еще использовал ключ -t для старта службы proxy.
Правило: использовать по-началу и для дебага только стандартный формат логов, и не добавлять к параметрам старта сервисов ключ -t, который убирает служебную инфу.
В общем немного помучавшись, и не имея ничего хорошего в логах, я все вернул обратно и опять пошел читать доку к 3proxy. Практически сразу зацепился за опцию maxconn, которая как на зло по-умолчанию равна 100 (в версиях с 0.7 - 500). Это ж я так никуда не уеду. Прописал в maxconn большее значение. Потом еще выяснил опытным путем, смотрев на вывод netstat -Lan, что в значениях maxconn и максимальном числе соединений (maxqlen) есть закономерность. maxqlen = (maxconn / 16) - 1. Так кратность будет ровной. Т.е., например, если maxconn = 2047, то maxqlen = 128.
Не знаю, может это так устроен unix или это заморочки 3proxy.
Ладно kern.ipc.somaxconn теперь равен 512, maxconn равен 8191. На второй день с утра с энтузиазмом повторил все действо с новыми настройками. Опять все то же самое - отваливается. Начал грешить на опции timeout. Поменял их на:
timeouts 10 15 30 60 180 1800 15 60
Третий день - те же пироги! Google Chrome вообще сказал "ERR_EMPTY_RESPONSE". Вернул все в зад на squid, т.к. не помогает timeout. В этот день я решил, что как-то совсем тупо и в лоб с этим всем пытаюсь разобраться. Вернулся к тестовому стенду, убрал -t из старта сервисов, переключался на стандартный формат логов, взял ab из apache в руки и натравил его на тестовую проксю. Типа такого и сразу несколько раз подряд:
# ab -k -r -n 50000 -c 5000 -X proxy-test.example.org:8080 'http://ya.ru/index.html'
И тут в логе 3proxy глаз зацепился за что-то необычное. Перемотал лог. Стрелять-колотить, вот оно!
pthread_create():_Resource_temporarily_unavailable
Ёмана, как все просто оказывается ;). 3proxy исчерпывает системный лимит тредов на один процесс. Sysctl'ка kern.threads.max_threads_per_proc говорит, что лимит по-умолчанию 1500. Есть еще одна хорошая sysctl переменная, которая показывает сколько раз в системе процессы достигали этого предела. Вот она - kern.threads.max_threads_hits. И конечно, у меня она не равна нулю. Глянул на продакшн сервер, который мучил утром - да, там она тоже ненулевая. Вот оно! Увеличил kern.threads.max_threads_per_proc до 3000.
На след. утро опять запили это все в продакшн и стал ждать наплыва пользователей. Все было лучше, чем в предыдущее утро. Ничего не отваливалось, но со временем начало работать все медленнее и медленнее. Я думал, что это как-то субъективно. Но нет, как только выключил 3proxy и включил squid, разницу сразу увидел. Это был как глоток свежего воздуха!
Тут я почти сдался, лениво начал что-то искать в интернете на эту и тему и где-то в очередном куске документации мелькнуло, что параметр nscache (величина DNS кэша) совсем не используется без задания опции nserver, указывающий на DNS сервер для резолва. У меня, конечно, кроме nscache, не было nserver в конфиге, причем я опустил это для простоты. Пущай из системы узнаёт. Не знаю, поможет ли. Ладно, задал nserver, не надеясь на успех.
После этого я потушил squid и тут же запустил 3proxy. Ура, все пошло как по маслу! Оказывается gethostbyname() в условиях такой нагрузки становится очень дорогим.
Я по началу думал еще, что squid так быстро работает из-за наличия у него веб-кэша. Но, как оказалось, 3proxy работает так же, если его настроить нормально =).
Так я оставил работать 3proxy как основной прокси еще на день. В процессе еще раз израсходовал количество допустимых тредов и процессов, после чего увеличил этот предел до 4000. Вообще в пиковое время ничего не тормозило и была такая картина (сокетов от socks службы там не больше 200):
# sockstat | grep 3proxy | wc -l
    5721
З.Ы. Когда 3proxy в превый раз не сдюжил нагрузку, это вообще было типа challende accepted =). Не могу успокоиться, пока не доведу дело до конца и не узнаю в чем проблема.

Конфиг 3proxy:

# cat /usr/local/etc/3proxy.cfg
external 0.0.0.0
internal 192.168.x.x
# It won't use nscache without nserver option
nserver 192.168.x.x
nserver 192.168.x.x
nscache 65536
#
timeouts 1 5 30 60 180 1800 15 60
daemon
log /var/log/3proxy/access.log D
# It's original log format, great for tracking down errors and much more
#logformat "- +_L%t.%.  %N.%p %E %U %C:%c %R:%r %O %I %h %T"
# It's squid log format, but it almost hides error messages
logformat "- +_G%t.%. %D %C TCP_MISS/200 %I %1-1T %2-2T %U DIRECT/%R application/unknown"
archiver gz /usr/bin/gzip %F
rotate 7
monitor "/usr/local/etc/3proxy.cfg" 
# HTTP proxy settings
#
flush
auth iponly
# These 3 lines show how to redirect
#allow * # Let's allow something and then extend it using parent directive
#parent 1000 http 192.168.2.233 8080 # to squid
#parent 1000 connect 192.168.25.5 3128 # to 3proxy
# !Example!
# allow/deny <userlist> <sourcelist> <targetlist> <targetportlist> <operationlist> <weekdayslist> <timeperiodslist>
deny * * $"/usr/local/etc/3proxy.block_domains"
deny * * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 20,21,70,80,81,85,88,210,280,443,488,563,591,777,1024-65535 HTTP
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 443,563,2083,2096,4074,4443,4445,5900,7772,8080,8087,8090,8443,8444,8510,9080,9443 HTTP_CONNECT
maxconn 8191
proxy -u -t 
# SOCKS proxy settings
#
flush
auth iponly
deny * * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.1 * * * *
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 20,21,70,80,81,85,88,210,280,443,488,563,591,777,1024-65535 * * *
maxconn 2047
socks -u -t

среда, 12 марта 2014 г.

Squid. Аутентификация и авторизация в AD. Грабли.

Продолжение темы http://timp87.blogspot.ru/2014/02/squid-ad.html

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

Так, поток сознания, может потом найду время и оформлю нормально.

1. Использование CNAME.

В интернетах пишут что с CNAME такое хозяйство не заработает. У меня в тестовой зоне и не только работает. Тут ситуация зависит от того, как этот CNAME используется. Всё работает, если keytab сделан для proxy.example.org, прокси-сервер имеет DNS имя server1.example.org, затем сделан CNAME proxy.example.org -> server1.example.org, и в браузере у пользователя в качестве прокси-сервера прописано proxy.example.org.

2. Обратная зона DNS.

Еще пишут, что для kerberos нужна рабочая обратная DNS зона (PTR). И вот это справедливо. Не было возможности проверить, касается ли это самого прокси сервера (у меня в обратной зоне для него прописано неправильное имя, и ничего, работает), но точно должны резолвиться в обратке контроллеры домена. У меня с этим проблем не было.
Они появились с пользователями из филиальских поддоменов, которые запрашивали доступ в интернет на нашей прокси. Например, NSK.EXAMPLE.ORG - Новосибирский филиал, или, KRAS.EXAMPLE.ORG - Красноярский филиал. В логе squid было видно, что такие пользователи проходят без проблем аутентификацию, но вот авторизация не шла.
В логе messages валились такие сообщения:
Mar 13 10:05:36 hostname (ext_kerberos_ldap_group_acl): GSSAPI Error:  Miscellaneous failure (see text)) unknown (Server (krbtgt/168.20.113@EXAMPLE.ORG) unknown)
Т.е. в имени тикета обрывок IP адреса контроллера филиальского домена вместо его (DC) полного имени.
В логе squid в это же время было такое:
kerberos_ldap_group.cc(437): pid=51536 :2014/03/13 06:22:09| kerberos_ldap_group: INFO: Got User: ipetrov Domain: NSK.EXAMPLE.ORG
support_sasl.cc(274): pid=51536 :2014/03/13 06:22:09| kerberos_ldap_group: ERROR: ldap_sasl_interactive_bind_s error: Local error
support_ldap.cc(869): pid=51536 :2014/03/13 06:22:09| kerberos_ldap_group: ERROR: Error while binding to ldap server with SASL/GSSAPI: Local error
support_sasl.cc(274): pid=51536 :2014/03/13 06:22:09| kerberos_ldap_group: ERROR: ldap_sasl_interactive_bind_s error: Local error
support_ldap.cc(869): pid=51536 :2014/03/13 06:22:09| kerberos_ldap_group: ERROR: Error while binding to ldap server with SASL/GSSAPI: Local error
support_sasl.cc(274): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: ldap_sasl_interactive_bind_s error: Local error
support_ldap.cc(869): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: Error while binding to ldap server with SASL/GSSAPI: Local error
support_sasl.cc(274): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: ldap_sasl_interactive_bind_s error: Local error
support_ldap.cc(869): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: Error while binding to ldap server with SASL/GSSAPI: Local error
support_sasl.cc(274): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: ldap_sasl_interactive_bind_s error: Local error
support_ldap.cc(869): pid=51536 :2014/03/13 06:22:10| kerberos_ldap_group: ERROR: Error while binding to ldap server with SASL/GSSAPI: Local error
Судя по всему, прокси не удается подключиться по ldap через GSSAPI к контроллеру домена, ответственному за филиальскую зону NSK.EXAMPLE.ORG, где он будет проверять наличие пользователя в определенной группе.
Искал решение пару дней. Спрашивал у филиальского контроллера какие схемы аутентификации он поддерживает:
# ldapsearch -H ldap://dc1.nsk.example.ru -x -b "" -s base -LLL supportedSASLMechanisms
dn:
supportedSASLMechanisms: GSSAPI
supportedSASLMechanisms: GSS-SPNEGO
supportedSASLMechanisms: EXTERNAL
supportedSASLMechanisms: DIGEST-MD5
Да, предлагает GSSAPI, тут все нормально. Еще можно посмотреть, а вообще у меня на прокси установлен GSSAPI SASL?
# pluginviewer | grep -i gssapi
  SCRAM-SHA-1 GSSAPI GSS-SPNEGO DIGEST-MD5 EXTERNAL OTP CRAM-MD5 NTLM LOGIN PLAIN ANONYMOUS
  SCRAM-SHA-1 GSSAPI GSS-SPNEGO DIGEST-MD5 OTP CRAM-MD5 NTLM LOGIN PLAIN ANONYMOUS
Plugin "gssapiv2" [loaded],     API version: 4
        SASL mechanism: GSSAPI, best SSF: 56, supports setpass: no
Plugin "gssapiv2" [loaded],     API version: 4
  SCRAM-SHA-1 GSSAPI GSS-SPNEGO DIGEST-MD5 EXTERNAL OTP CRAM-MD5 NTLM LOGIN PLAIN ANONYMOUS
  SCRAM-SHA-1 GSSAPI GSS-SPNEGO DIGEST-MD5 EXTERNAL OTP CRAM-MD5 NTLM LOGIN PLAIN ANONYMOUS
Plugin "gssapiv2" [loaded],     API version: 4
        SASL mechanism: GSSAPI, best SSF: 56
Plugin "gssapiv2" [loaded],     API version: 4
И тут все ок. Решил, что раз в тестовой зоне все работает, значит чем-то она отличается от промышленной. Не помню почему, но первым делом я взял в зубы tcpdump и стал смотреть, что происходит в сети между всеми контроллерами в тестовой и промышленной зонах. Тут я и нашел проблему. Все оказалось довольно просто: единственное отличие было в том, что тестовая прокся могла отрезолвить филиальский контроллер домена по IP, а вот промышленная - нет. Т.е. в промышленной зоне у меня не было филиальских контроллеров домена в обратной зоне!

Небольшое отступление: тут нужно понимать как это все работает (именно авторизация пользователя на прокси!). У меня не было четкого осознания, поэтому я довольно долго бился. Чтобы прокси получила доступ к инфе о свойствах пользователя, прокси надо спросить по LDAP у контроллера домена филиала этого пользователя о нем. Данный хелпер устроен так, что он аутентифицируется на контроллере тоже через kerberos. Т.е. для начала он должен получить тикет на доступ к службе ldap/dc1.nsk.example.org. Потом уже с помощью него аутентифицируется на dc1.nsk.example.org и запрашивает по ldap что хочет. Так что когда прокси спрашивал запись NSK.EXAMPLE.ORG типа A, ему сообщали IP адреса филиальских контроллеров. Затем он пытался получить имена филиальских контроллеров через эти IP адреса, чтобы знать, какой тикет ему получать. Вот на этом шаге все ломалось.

Вывод: все контроллеры доменов должны быть в обратной DNS зоне!

3. Всякий разный софт.

Огромное количество всякого софта (не браузеры), который хочет в интернет и при этом, что неудивительно, не умеет kerberos, или вообще никак не умеет аутентифицироваться на прокси.
Вообще это больше административный вопрос: каким ПО можно пользоваться в компании. Если все лояльно и пользователи могут ставить и пользоваться любым софтом, и при этом рассчитывать на то, что оно все будет работать, а если и не работает, то звонить ИТшникам и требовать починить, то вы огребете немеряно проблем. Поэтому всю эту штуку не стоит включать вообще для всех пользователей. Стоит включать только для части.
Для отладки нужно много смотреть в логи. Вот полезная ссылка с расшифровкой всех кодов, которые могут появиться в логах http://wiki.squid-cache.org/SquidFaq/SquidLogs

Дальше я немного окунулся в тот ад, который бы ждал меня, если аутентификация включена вообще для всех и список разрешенного софта в компании не определен, т.е. работать должно всё.
Тут есть несколько вариантов решения проблем с таким софтом:
a) пускать софт через другую прокси, где это можно. Например, поднять отдельный socks5 прокси;
b) выпускать в инет по user-agent до правила аутентификации. Смотреть acl типа browser.
c) выпускать в инет по IP до правила аутентификации. Тема озвучена в предыдущем посте.
d) выпускать сразу по порту до правила аутентификации? надо проверить.
e) выпускать по домену, на который обращается софт, до правила аутентификации.

В этот далеко неполный список проблемного софта попадают:

- прорва банк-клиентов совершенно разного типа. Тут все сложно. Нужен индивидуальный подход. Можно выпускать компы с банк-клиентами по IP;
- специфические софтины, например, конфигуратор телефонных станций Avaya. Не придумал что делать;
- Всякие задания от ERP системы, которым тоже зачем-то надо в интернет. Можно по IP;
- Google Drive не говорит User-Agent, из настроек прокси есть только "Автоматически определять" и "Прямое подключение". Никакой вообще аутентификации не умеет. (старый топик на тему + описание). Выпускать напрямую без прокси.
- Mail.ru Cloud и User-Agent сообщает, и настройки прокси имеет, где можно указать тип none/https/socks4/socks5 и базовую аутентификацию. Можно и через user-agent и через socks выпускать.
- MS SkyDrive (OneDrive) вообще заработал сам по себе, хотя user-agent не говорит и настроек прокси у него нет.
- Ammyy Admin не говорит User-Agent, имеет очень скудные настройки HTTP-прокси (socks нет). Выпускать напрямую.
- Skype заработал сам собой (выставлено "автом. обнаружение прокси"). Говорит много всяких User-Agent, имеет настройки прокси.
- Firefox заработал сам собой (выставлено "использовать системные настройки прокси"), есть куча настроек.
- всякие браузерные Java-аплеты для тех или иных целей, которые запускаются из браузера. Как правило, говорят нормальный User-agent. По нему можно выпускать в инет с прокси.
- iTunes говорит нормальный User-Agent! Но совершенно не имеет никаких настроек прокси, и тупо берет их из IE. Без вариантов. Выпускать по User-Agent.
- 1c приложения, которые хотят свежие курсы валют и т.д. Как правило, говорят нормальный User-agent. Выпускать по User-Agent.
- Valve Steam говорит user-agent. Настройки прокси тянет из системы. Но вот когда синхронизируется со своим облаком - не говорит User-Agent. Не знаю что делать.
- Старая Opera (версии <= 12). Да, есть еще люди, которые ей пользуются и в ус не дуют. Говорит User-Agent, но не всегда, из-за чего падает, если выпускать по user-agent . Имеет настройки прокси, но скудные. Пускать через socks. Но по-хорошему ее (<=12) надо запретить на административном уровне.
- DropBox не говорит User-Agent, но имеет настройки прокси, где можно указать тип none/https/socks4/socks5 и базовую аутентификацию. Выпускать через socks5.
- QIP (и прочие "аськи") не говорит нормального User-Agent, имеет настройки прокси, где можно указать тип none/https/socks4/socks5 и базовую аутентификацию. Выпускать через socks5.
- Mail.ru Agent, смотри предыдущее.
- Яндекс.Диск не говорит User-Agent, зато имеет настройки прокси, где можно указать тип none/https/socks4/socks5 и базовую аутентификацию. Выпускать через socks5.


Пока мой предполагаемый и не полностью проверенный список User-Agent'ов выглядит так:
+ проверял работоспособноть;
- не проверял;

# User-agent ACLs
# These software can't do kerberos
# + Apple iTunes
acl no_auth_ua browser ^iTunes/[0-9].*AppleWebKit/[0-9]
# - Various Java applets
acl no_auth_ua browser Java/[0-9]
# - Avaya telephony station configurator (Shalagin, Putalov, etc.). It has to be checked
acl no_auth_ua browser Avaya Solution Designer
# - 1C software
acl no_auth_ua browser 1C:Enterprise
# - Apple iCloud
acl no_auth_ua browser ^iCloud.*CFNetwork/[0-9]
acl no_auth_ua browser ^ApplePhoto.*CFNetwork/[0-9]
acl no_auth_ua browser ^Microsoft-CryptoAPI/[0-9]
# + Cloud Mail.ru
acl no_auth_ua browser ^CloudDesktopWindows.*[0-9]
# +/- Valve Steam
acl no_auth_ua browser Valve.*Steam.*Client.*[0-9]


Чуть ниже результат небольшого исследования при использовании какого софта какие User-Agent появляются в логах:

# Don't know what is it
Microsoft Office/14.0 (Windows NT 6.1; Microsoft Outlook 14.0.7106; Pro)
OutlookSocialConnector/1.0
Microsoft SettingsSync (6.3.9600.16500) Windows NT 6.3 (0603) Desktop IsNotAoAc
Microsoft-WNS/6.3
VCSoapClient
MSDW
Windows-Update-Agent
Microsoft BITS/7.5
Chrome WIN 33.0.1750.149 (255236) channel(stable)
# Related to Java applets
Mozilla/4.0 (Windows 7 6.1) Java/1.7.0_51
Java/1.7.0_51
# Mail.ru Cloud
CloudDesktopWindows 14.02.1200 beta (6.1)
# Skype
Microsoft-CryptoAPI/6.1
Skype▒ 6.14
WinINet HttpStack/11
# iTunes
Microsoft-CryptoAPI/6.1
GCSL GCSP 2.0
iTunes/11.1.5 (Windows; Microsoft Windows 7 x64 Business Edition Service Pack 1 (Build 7601)) AppleWebKit/537.60.15
# iCloud
Microsoft-CryptoAPI/6.3
ApplePhotoStreams.exe (unknown version) CFNetwork/520.3.4
ApplePhotoStreamsDownloader.exe (unknown version) CFNetwork/520.3.4
iCloud.exe (unknown version) CFNetwork/520.3.4
# uTorrent
uTorrent/3320(30486)
BTWebClient/3320(30486)
# Valve Steam
Valve/Steam HTTP Client 1.0
Breakpad/1.0 (Windows)
Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; Valve Steam Client/1386799583; ) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19
# Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1003.1 Safari/535.19 Awesomium/1.7.1
# Steam Cloud uses no UA, but do "CONNECT steamcloudams.blob.core.windows.net:443"

4. Итог

Я подумал, что каждый раз, когда что-то у пользователей не работает, ковыряться и разбираться почему и как сделать счастье, это трудозатратно и долго.  И в общем-то на уровне всей сети нам не нужно запрещать доступ в инет. Запрещать нужно только для некоторых пользователей, которые работают через наши терминальные серверы. Поэтому мы включили аутентификацию только для определенных IP.
Поскольку squid проверяет списки доступа сверху вниз и слева направо, то в строчку с вызовом аутентификации http_access нужно добавить acl с нужными IP, да только так, чтобы он стоял там раньше всех. Вот так оно вошло в конфиг:
+acl AuthIP src 192.168.2.22 192.168.2.23  
-http_access deny NoInetAD all
+http_access deny AuthIP NoInetAD all
http_access allow all
Т.е. сначала будет сравниваться IP адрес (AuthIP), а уже при совпадении вызываться аутентификация (NoInetAD).
Сейчас живем так, в ус не дуем.

З.Ы. некоторое развитие темы проксирования 

среда, 5 февраля 2014 г.

Squid. Аутентификация и авторизация в AD.

Не так давно появилась задача разграничения доступа пользователей в интернет. Причем, всем в инет можно, кроме небольшой (относительно всей компании) группы людей.
В конторе у нас в качестве пользовательской ОС используется Windows (7+). Конечно, есть AD (2008+). Есть еще домены филиалов, с которыми установлены доверительные отношения (не помню, вроде только в одну сторону).
Надо поднимать web proxy. Требования в такой ситуации напрашиваются сами собой: нужно аутентификацию и авторизацию для прокси брать из AD (не путай эти понятия!).
В качестве инструментов хотелось использовать FreeBSD и squid.
Пошел я гуглить и искать инфу. В сети огромное количество howto'шек разной степени бестолковости. Кто в лес, кто по дрова, не понимая, нужны ли эти дрова вообще и куда их пихать. По кусочкам кое-что получилось понять, а дальше ваял сам. Тут результат понимания и описание процесса, чтобы сам потом мог легко вспомнить.
В итоге доступ в инет был организован для пользователей домена, а также, некоторых сетей (устройств), где не нужна аутентификация/авторизация.

Все по понятиям

Потенциально аутентификация в моем случае возможна несколькими способами:
- NTLM
- LDAP
- Kerberos
Первый - этакая попытка Microsoft'а сделать свой Kerberos. Но сейчас его уже закапывает сам Microsoft, отказываясь от него в пользу третьего. Да и сам по себе протокол плохой (да, да, хэш от пароля ходит по сети). Второй неплох, но требует от пользователя ручного ввода пароля при аутентификации, т.е. браузер будет просить ввести логин/пасс выводя окошко. А вот Kerberos - то, что нужно. Он безопасный и все будет выглядеть прозрачно: никаких предложений ввести пароль и т.д. Правда, он чуть сложнее в настройке, чем предыдущие варианты, и старые версии Windows его не умеют.
Авторизация - через LDAP. Других способов я не знаю. Т,е. в AD есть security группа, в которую добавлены учетные записи (или другие группы, см. ниже) неугодных пользователей.
Разворачивал все это дело я на FreeBSD 10.0-RELEASE amd64. В качестве прокси - squid. Весь софт ставился из свежих портов. На Linux'е весь смысл тот же, только софт называется немного иначе.
Все это хозяйство настраивал и проверял по частям, сначала сделал аутентификацию, проверил ее, потом сверху примостил авторизацию, проверил, а потом сделал некоторые корректировки.

База

Первым делом я разбирался с тем, как работает Kerberos. Это очень помогло в понимании того, что нужно делать. Как пример http://itband.ru/2010/12/kerberos1/ или  http://www.oszone.net/4188/Kerberos. Из описания следует требование: время на серверах и клиентах должно быть синхронизировано. Проверил время, автозапуск ntpd и т.д. Конечно, нужен еще работающий DNS.
Далее осмыслив работу Kerberos я понял, что сервер, предоставляющий сервис, на котором аутентифицируется доменный клиент тоже должен аутентифицироваться. С пользователем все просто: у него есть учетка в AD, в которой записаны логин и хэш от его пароля. Хэш от пароля и есть закрытый ключ пользователя, которым он шифрует некоторые части пакетов при аутентификации на KDC. Пользователь знает свой пароль и клиентский софт извлекает из него хэш. KDC тоже знает хэш пользователя. И тот и другой может зашифровать и расшифровать пакет этим ключом. Но что с серверами? Сервер же тоже должен иметь какой-то закрытый ключ, который должен знать как KDC, так и сам сервер.

Создание учетки для прокси в AD

Вообще, когда виндовые серверы заводятся в домен, у них появляется "учетка" в AD типа computer, для которой, скорей всего (честно, не знаю), формируется на каком-то шаге (может при развертывании какого-либо сервиса/роли) закрытый ключ. Потом этот ключ как-то попадает на этот новый сервер, я предполагаю. В моем случае сервер не виндовый, и мне нужно сделать это вручную.
В сети можно найти много статей где прокси *nix-сервер добавляют в домен, перед этим долго и мучительно настраивая samba. Это совсем необязательно. Это делается только для того, чтобы получить из AD файл keytab для "учетки" типа computer, от которой будет аутентифицироваться прокси сервер. Keytab, насколько я понял - это что-то вроде контейнера с закрытыми ключами, которыми будет пользоваться демон/сервис (squid) при аутентификации по Kerberos'у.
Есть 2 пути создания "учетки" в AD для сервера: добавление сервера в домен c помощью samba с самого *nix сервера (так создается "учетка" типа computer), или можно создать обычного пользователя (тип user) в AD любыми средствами, например, оснасткой "AD users and computers". Понятно, что второй способ проще.
Затем есть 3 способа извлечения файла keytab для созданной "учетки": с помощью samba на *nix сервере (сервер должен быть в домене), утилитой mskutil на *nix сервере, или утилитой ktpass на контроллере домена. Опять же, последний мне показался проще.
Я создал пользовательскую учетку с именем "squid", задал ей пароль "passWD". По нашим правилам я создал ее в контейнере "Special accounts", который находится на вершине дерева AD. После чего экспортировал keytab с помощью ktpass так (описание опций читай в хелпе):
> ktpass -princ HTTP/proxy.example.org@EXAMPLE.ORG -mapuser SAMPLE\squid -pass passWD  -crypto all -ptype KRB5_NT_SRV_HST -out squid.keytab
Предположим, что мой домен назван "example.org", а NetBios-имя моего домена "sample". Контроллеры домена называются dc1.example.org и dc2.example.org. Сам сервер, который я настраивал зовется proxy.example.org.
На выходе ktpass получился файл squid.keytab. Его нужно скопировать на *nix сервер.

Настройка Kerberos на FreeBSD

Во FreeBSD уже есть в базовой системе реализация Kerberos'а, так что ничего дополнительного устанавливать для его работы не надо, только настроить. Вообще в мире opensource *nix реализаций этого протокола 2 штуки: Heimdal и MIT. Первая чаще используется в BSD, вторая в Linux.
Все настройки Kerberos'а хранятся в файле /etc/krb5.conf (надо создать его). Туда для моего домена нужно записать:
[libdefaults]
 default_realm = EXAMPLE.ORG
 default_keytab_name = /usr/local/etc/squid/squid.keytab <--- тот самый из-под ktpass
[realms]
 EXAMPLE.ORG = {
 kdc = dc1.example.org
 kdc = dc2.example.org
 admin_server = dc1.example.org
 default_domain = example.org
 }
[domain_realm]
 .example.org = EXAMPLE.ORG
 example.org = EXAMPLE.ORG
Тут все более-менее понятно, если что, можно глянуть в ман krb5.conf(5). Строка default_keytab_name указывает на файл keytab, который будет использоваться по-умолчанию всеми службами, которым нужен Kerberos, если ничего другого не указано. Другой способ задания файла keytab - в переменных окружения конкретного демона, например, в скрипте запуска этого демона(как тут http://wiki.squid-cache.org/ConfigExamples/Authenticate/Kerberos). Т. е. там можно для каждого демона задать свой keytab. Замечу, что под FreeBSD можно просто добавить строку squid_krb5_ktname="path/to/keytab" в /etc/rc.conf. У меня прокси не будет выполнять других функций, для которых нужен Kerberos, так что я задал keytab в /etc/krb5.conf.
Теперь, когда все настроено и на месте можно посмотреть в keytab на предмет поддерживаемого шифрования:
# ktutil -k /usr/local/etc/squid/squid.keytab list
/usr/local/etc/squid/squid-v.keytab:
Vno  Type                     Principal                  Aliases
  4  des-cbc-crc              HTTP/proxy.example.org@EXAMPLE.ORG
  4  des-cbc-md5              HTTP/proxy.example.org@EXAMPLE.ORG
  4  arcfour-hmac-md5         HTTP/proxy.example.org@EXAMPLE.ORG
  4  aes256-cts-hmac-sha1-96  HTTP/proxy.example.org@EXAMPLE.ORG
  4  aes128-cts-hmac-sha1-96  HTTP/proxy.example.org@EXAMPLE.ORG
А потом проверить возможность аутентификации в AD через Kerberos:

# kinit -k HTTP/proxy.example.org
# klist
Credentials cache: FILE:/tmp/krb5cc_0
        Principal: HTTP/proxy.example.org@EXAMPLE.ORG
  Issued                Expires               Principal
Feb  7 14:37:49 2014  Feb  8 00:37:44 2014  krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
# kdestroy
Получил тикет, посмотрел на него, удалил.

Установка squid

Squid я ставил из портов, конкретно www/squid33. Был он версии 3.3.11. Я уверен, что когда кто-то будет читать эти строки, версия будет новей и порт будет называться www/squid. Тут было несколько нюансов. Сильно забегая вперед:
изначально, для настройки аутентификации и авторизации я ставил squid, добавив к стандартным только опции AUTH_LDAP и AUTH_KERB (ныне опция называется GSSAPI_* в зависимости от выбираемой реализации). Как оказалось потом, с аутентификацией все было хорошо, но с авторизацией пришлось повозиться. Вышло так, что хелпер ext_ldap_group_acl (опция AUTH_LDAP) не умеет работать с вложенными группами. Т.е. он просто проверяет наличие пользователя в конкретной группе. Если в группе запрета интернета есть вложенная группа, а уже во вложенной наш пользователь, то ext_ldap_group_acl этого не узнает. Не удобно, ведь не хочется добавлять в группу запрета интернета  пользователей по одной учетке, хочется же целыми отделами =) или группами из филиальских доменов. Пришлось еще погуглить и нашелся другой хелпер ext_kerberos_ldap_group_acl. (стандартно он называется squid_kerb_ldap), который умеет такую штуку, только наоборот, выполняет рекурсивный поиск нужной группы на основе списка членства в группах (MemberOf) самого пользователя. Только он не был у меня установлен. И как его доустановить штатными средствами я не сразу понял. Нужно было только заглянуть в Makefile порта www/squid33 и все стало понятно. Для установки этого хелпера нужна комбинация опций AUTH_LDAP и AUTH_SASL. После переустановки порта, хелпер появился, но не работал. В логе squid'а были сообщения вроде таких:
ERROR: ldap_sasl_interactive_bind_s error: Can't contact LDAP server
ERROR: Error while binding to ldap server with SASL/GSSAPI: Can't contact LDAP server
Тут всплыли еще подробности нужно было установить openldap-client с поддержкой sasl. Для этого вместо net/openldap24-client нужно поставить net/openldap24-sasl-client. Можно еще добавить WANT_OPENLDAP_SASL=yes в /etc/make.conf чтобы потом проблем не было.
Потом опять ничего не заработало:
ERROR: ldap_sasl_interactive_bind_s error: Unknown authentication method
ERROR: Error while binding to ldap server with SASL/GSSAPI: Unknown authentication method
В /usr/ports/UPDATING нашел, что плагин gssapi был отделен от основного порта cyrus-sasl2. Доустановил плагин security/cyrus-sasl2-gssapi. Наступило счастье =).

Настройка браузера.

Тут ест одно важное замечание: в качестве адреса прокси у пользователей в браузере должно быть указано полное dns имя сервера, например, proxy.example.org. Также будет работать если dns запись является CNAME.
А вот с забитым в браузере IP прокси сервера ничего работать не будет!

Настройка squid

Действовал я пошагово, поэтому первым делом настроил и проверил аутентификацию:
auth_param negotiate program /usr/local/libexec/squid/negotiate_kerberos_auth -s HTTP/proxy.example.org@EXAMPLE.ORG
auth_param negotiate children 50 startup=10 idle=5
auth_param negotiate keep_alive on
Все очевидно. Для справки можно смотреть в доку squid'а или вывод "negotiate_kerberos_auth -h". Скажу лишь, что на этапе отладки лучше всего перед опцией -s добавить -i, так в логе cache.log появится много интересной инфы. Ссылка, где в конце есть схема работы squid с Kerberos http://wiki.squid-cache.org/ConfigExamples/Authenticate/Kerberos.
Есть вариант проверки работоспособности хелпера, который я подсмотрел у Markus Moeller, выводящий огромное количество информации. Нужно в командной строке от рута запустить:
# kinit username@EXAMPLE.ORG
# /usr/local/libexec/squid/negotiate_kerberos_auth_test proxy.example.org 1 | awk '{sub(/Token:/,"YR"); print $0}END{print "QQ"}' | /usr/local/libexec/squid/negotiate_kerberos_auth -d
Надо не забыть потом запустить kdestroy.
Затем я разобрался с авторизацией. Это конфиг:
external_acl_type NoInet ttl=3600 negative_ttl=3600 children-max=50 children-startup=10 children-idle=5 grace=15 %LOGIN /usr/local/libexec/squid/ext_kerberos_ldap_group_acl -a -i -g DenyInternet -m 64 -D EXAMPLE.ORG -u squid -p passWD
Опять же, все есть в хелпе и доке. На время дебага можно еще перед -i добавить -d. Замечу опцию -m, указывает уровень рекурсии при поиске группы, и еще опцию grace, которая хорошо ускоряет авторизацию (читай в доке). DenyInternet - это группа из AD, в которой томятся неугодные юзеры. NoInet - это имя данного внешнего acl.
Проверить работоспособность этой части можно просто запустив хелпер с нужными опциями, т.е. копируем в командную строку все, что после %LOGIN. Затем вводим имя пользователя и получаем в ответ OK или ERR. Например, из командной строки:
# /usr/local/libexec/squid/ext_kerberos_ldap_group_acl -a -i -g DenyInternet -m 64 -D EXAMPLE.ORG -u squid -p passWD
username@EXAMPLE.ORG
OK 
Пользователь входит в группу, иначе получили бы ERR.
Остальная часть конфига:
cache_dir diskd /var/squid/cache 25600 16 256
acl NoAuthNet src 192.168.8.0/24
acl NoAuthDevice src 192.168.8.184/32
acl NoInetAD external NoInet
http_access allow NoAuthDevice
http_access allow NoAuthNet
http_access deny NoInetAD all
http_access allow all
http_port 3128
cache_mgr helpdesk@example.org
Первая строчка описывает механизм работы squid'а с кэшем. В данном случае используется отдельный демон для дисковых операций с кэшем. Для его корректной работы нужно изменить некоторые sysctl'ки. Тут пример каких, и откуда они взялись https://forums.freebsd.org/viewtopic.php?f=43&t=44638. Однако, есть мнение, что все, кроме ufs "сломано" и нестабильно.
Затем описание acl'ей. Тут есть девайс и сеть, для которых аутентификация и авторизация не нужны. Потом привязываем внешний acl из настройки авторизации к другому acl NoInetAD.
Теперь собственно описание доступа (принцип работы как в ipfw). Проверка условий идет по порядку сверху вниз, слева направо, поэтому первым делом выпускаем в интернет сеть NoAuthNet и устройство NoAuthDevice.
Затем для тех кто не совпал с предыдущими правилами вызываем авторизацию, которая в свою очередь вызовет аутентификацию, т.е. не пускаем неугодных юзеров, которые состоят в группе в AD. Здесь нужно заметить, что не случайно написано слово deny, а не allow. Дело в том, что allow работает только для уже аутентифицированных пользователей и не вызывает повторный запрос на аутентификацию, если она до этого момента не состоялась. Отправку ответа "407 - Proxy authentification required" выполняет только deny. Еще можно заметить, что в конце стоит all. Это не просто так, т.к. если в описании http_access последним стоит acl с авторизацией/аутентификацией, то она будет запрашиваться снова и снова, вместо одного раза. Поэтому, если ее не будет, то неугодным пользователям будет показываться окно авторизации раз 5. Некрасиво. А так они сразу получают Access denied . Все эти особенности были вычитаны тут http://wiki.squid-cache.org/Features/Authentication. Там описано еще несколько полезных трюков по аутентификации.
И последней строчкой разрешаем выход в инет.
Вот полезная ссылка про правила работы squid'а с списками доступа http://wiki.squid-cache.org/SquidFaq/SquidAcl

На всякий случай

Не относится к всему посту, просто мало ли когда-нибудь пригодится портянка с аутентификацией и авторизацией простым LDAP:
# Authentification using LDAP
##Delete the comments to enable another one auth procedure if previous wasn't passed.
##auth_param basic program /usr/local/libexec/squid/basic_ldap_auth -R -D squid@example.org -W /usr/local/etc/squid/ldap.txt -b "dc=example,dc=org" -f "sAMAccountName=%s" 192.168.1.2
#
# Authorization using LDAP
# -K - remove realm from username
# -d - turn on the debug
#external_acl_type InetGroup ttl=600 %LOGIN /usr/local/libexec/squid/ext_ldap_group_acl -R -b "dc=example,dc=org" -f "(&(objectclass=person)(sAMAccountName=%v)(memberOf=cn=%a,ou=Security Groups,dc=example,dc=org))" -D
 squid@example.org -W /usr/local/etc/squid/ldap.txt -K -d 192.168.1.2 192.168.1.3
Тут нужно помнить и указывать путь к контейнеру, где лежит служебная учетка squid.

А вот продолжение, которое я написал после некоторого срока эксплуатации такой схемы http://timp87.blogspot.ru/2014/03/squid-ad.html.