§ SSH ключи в Active Directory.

Сразу хочу предупредить, что данный пост скорее описание техничесской возможности хранения SSH ключей в Active Directory, и пошаговая рекомендация к тому как это сделать. К сожалению я не нашел нормального описания данного механизма, и поэтому взялся описать то к чему пришел сам листая многочисленные статьи и документацию по SSH, AD и FreeIPA. Сразу скажу что никакого FreeIPA ставить не придется, а хранить сами ключи мы будем в поле Notes вкладки Telephones. Потенциально нам ничего не мешает сделать отдельный атрибут в AD для этого, но я иду максимально простым путем.

Так как внутри нашей инфраструктуры установлен CentOS, то дружить с AD мы будем именно его. 

Схема работы.

Схема работы проста, и достаточно очевидна, однако не вполне очевидна с точки зрения реализации. Итак по порядку

  1. Клиент инициирует подключение к SSHd при помощи ключа.
  2. Сервер SSHd проверяет корректность подключаемого пользователя при помощи SSSD (активный аккаунт, существующее имя, Allow|Deny листы)
  3. Сервер SSHd передает управление скрипту ldap_pubkey который возвращает в STDOUT содержимое аналогичное файлу authorized_keys. В нашем случае мы используем один публичный ssh ключ.
  4. Сервер предоставляет доступ, если имя пользователя и ключ верны.

Как видно все достаточно просто, поэтому приступим.

Установка и настройка SSSD.

Первым делом мы должны установить и настроить сервис sssd для авторизации наших пользователей при помощи AD

# yum install sssd

Дальше мы создаем файл /etc/sssd/sssd.conf примерно следующего содержания

[sssd]
config_file_version = 2
reconnection_retries = 3
sbus_timeout = 30
services = nss, pam
domains = domain.com

[nss]
filter_groups = root
filter_users = root
reconnection_retries = 3

[pam]
reconnection_retries = 3

[domain/domain.com]
# Defaults
debug_level = 0
cache_credentials = false
enumirate = true

# Providers
id_provider = ldap
auth_provider = ldap
ldap_schema = ad
ldap_id_mapping = true

ldap_uri = ldaps://ad.domain.com:636
ldap_search_base = OU=Users,DC=domain,DC=com
ldap_user_search_base = OU=Users,DC=domain,DC=com
ldap_group_search_base = OU=Groups,DC=domain,DC=com
ldap_tls_reqcert = allow
ldap_idmap_range_min = 5000

# Fallback
fallback_homedir = /home/%u
shell_fallback = /bin/bash

# AD mapping
ldap_user_name = sAMAccountName
ldap_user_object_class = user
ldap_group_object_class = group

# Bind auth
ldap_default_bind_dn = user@domain.com
ldap_default_authtok = SecurePassword

Для того что бы сервис sssd запустился и позволял нам авторизовать наших пользователей необходимо выполнить следующие комманды

# chmod 600 /etc/sssd/sssd.conf

# authconfig --enablesssd --enablesssdauth --enablelocauthorize --update

# chkconfig sssd on

# service sssd restart

После этого сервис sssd будет перезапущен и мы можем проверять работает он или нет. Проверка достаточно простая

# getent passwd username

username:*:246009170:246005513:Users Fullname:/home/username:

если мы видим подобный вывод (разумеется с реальным пользователем из AD) то значит что наш sssd работает. Я не буду тут подробно рассматривать как настраивать sssd, этому в сети посвящено огромное количество статей. Но дальше будет немного уличной магии.

Авторизация по ключу.

Итак, теперь наши пользователи из AD могут авторизоваться на нашем сервере используя свои логины и пароли. Но весь сырбор мы затеивали что бы пользователи могли авторизоваться используюя так же ssh ключ. Для этого ребята из RedHat пошли на определенную хитрость. Как известно RedHat сейчас крайне озабочен вопросами централизации, и для этой цели выпустил два продукта RedHat IPA и FreeIPA для управления всем. Не вдаваясь в подробности FreeIPA это сочетание DNS, DHCP, Samba и LDAP, при этом активно автоматизированное при помощи говна и палок в виде скриптов на Python плюс вебморда. Из коробки sssd прекрасно работает с FreeIPA, при этом принося в систему одну очень интересную утилиту sss_ssh_authorizedkeys. Данный бинарник предназначен для демона sshd, который может вызывать его передавая в качестве ключа имя пользователя, а в замен бинарник лезет в FreeIPA LDAP, где забирает пользовательских SSH ключ или ключи, и выплевывает их в STDOUT. В самом FreeIPA LDAP ключи хранятся в аттрибуте ipaSshPubKey, однако по быстрому выяснить что это за атрибут, какие параметры он содержит и тд и тп мне лично не удалось. Создание атрибута ipaSshPubKey в AD так же не помогло заставить этот бинарник заработать, поэтому я поступил проще - написал свой собственный скрипт на ruby, который делает ровно тоже самое - лезет в LDAP и выплевывает значение атрибута (в моем случае аттрибута info) в STDOUT. Итак ставим руби:

# yum install epel-release

# yum install ruby ruby-ldap rubygem-net-ldap

после этого можно создавать сам скрипт. Я создал его в /usr/bin/ldap_pubkey

#!/usr/bin/ruby

require 'rubygems'
require 'net/ldap'

exit 0 if ARGV == []

username = ARGV[0]

config = {
    :host       => "ad.domain.com",
    :port       => 389,
    :username   => "username@domain.com",
    :password   => "SecurePassword",
    :base       => "OU=Users,DC=domain,DC=coml",
}

ldap = Net::LDAP.new    :host => config[:host],
                        :port => config[:port],
                        :auth => {
                            :method => :simple,
                            :username => config[:username],
                            :password => config[:password],
                                 }

filter = Net::LDAP::Filter.eq "sAMAccountName", username
attributes = ["info"]

ldap.search(:base => config[:base], :filter => filter, :attributes => attributes ) do |entry|
    puts entry[:info].first
end

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

# /usr/bin/ldap_pubkey username

Если у пользователя в поле Notes был вписан ssh ключ - то вы его увидете.

Настраиваем SSHd.

Итак последний этап настройки - это SSH сервер. Дело в том что по умолчанию сервер SSHd не умеет работать со сторонними программами для получения SSH ключей, однако ребята из RedHat пропатчили SSHd, на RHEL и Fedora (а значит и на CentOS), и в конфигурации /etc/ssd/sshd_config теперь есть параметр "AuthorizedKeysCommand none". Нам его нужно раскоментировать и вписать туда наш скрипт, что бы получилось так

AuthorizedKeysCommand /usr/bin/ldap_pubkey

Но и это ещё не все. При первом логине домашняя дирректория пользователя не будет создана, поэтому нам нужно вписать ещё одну строчку в файл /etc/pam.d/sshd

session    required   pam_mkhomedir.so skel=/etc/skel/ umask=0022

Ну и на последок отключить SELinux, ну или сделать так что бы он не мешал созданию домашней дирректории.

В Active Directory.

Теперь дело осталось за малым - добавить ключ в AD. Для этого берем свой ключ

$ cat ~/.ssh/id_rsa.pub

и копируем его в атрибут Notes вкладки Telephones пользователя как есть. Если хотите что бы ключей было несколько - копируйте несколько ключей, но каждый новый ключ с новой строки.

Теперь когда вы захотите попасть на сервер по SSH используя свой ключ вы увидете что при первом логине сервис SSHd создал домашнюю дирректорию пользователя.

На последок

Стоит пожалуй повторить что данная схема хотя и сырая, тем не менее вполне рабочая, и показывает каким образом можно хранить ключи SSH в AD. Думаю что многим это будет полезно, ну или хотя бы интересно. В качетсве усовершенствования схемы можно добавлять отдельные атрибуты в AD, писать проверки в скрипт (сейчас их там одна), и полировать решение дальше. Сейчас эта схема проходит тестовую обкатку в нашей организации, и я думаю что со временем я допишу тут более менее существенные замечания и изменения. Так что следите.


comments powered by Disqus