2008/09/11

Использование LDAP-базы пользователей в Tomcat

Для аутентификации и авторизации в Tomcat очень удобно использовать LDAP-директорию. Для примера можно рассмотреть доступ к manager'у приложений, который идёт в комплекте с Tomcat (http://servername:port/manager/html). Данное приложение пропускает только тех пользователей, которые прошли аутентификацию и имеют роль manager.
Прежде всего необходимо подготовить LDAP-директорию, она должна иметь примерно такую структуру
dn: o=My Organization,c=RU
o: My Organization
objectClass: organization
objectClass: top

dn: ou=People,o=My Organization,c=RU
objectClass: top
objectClass: organizationalUnit
ou: People

dn: uid=user3,ou=People,o=My Organization,c=RU
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
sn: Test
uid: user3
userPassword: password3
cn: User 3

dn: uid=user2,ou=People,o=My Organization,c=RU
cn: User 2
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
sn: Test
uid: user2
userPassword: password2

dn: ou=groups,o=My Organization,c=RU
objectClass: organizationalUnit
objectClass: top
ou: groups

dn: cn=manager,ou=groups,o=My Organization,c=RU
cn: manager
objectClass: groupOfUniqueNames
objectClass: top
uniqueMember: uid=user3,ou=People,o=My Organization,c=RU
После этого в конфигурационном файле TOMCAT_HOME/server.xml необходимо закомментировать использование ресурса UserDatabase и UserDatabaseRealm, вместо него добавить
<realm classname="org.apache.catalina.realm.JNDIRealm"
connectionurl="ldap://LDAP_SERVER"
rolebase="ou=groups,o=My Organization,c=RU"
rolename="cn"
rolesearch="(uniqueMember={0})"
userpattern="uid={0},ou=People,o=My Organization,c=RU"/>

и перезапустить Tomcat.
Если всё получилось, доступ к ресурсу http://servername:port/manager/html будет иметь только пользователь user3, пользователь user2 хоть и пройдет аутентификацию, но доступа к ресурсу не получит, так как не включен в группу manager (не имеет данной роли).

2008/06/19

Репликация в MySQL

Срочно понадобилось иметь копию двух таблиц (test_db.wiz_main и test_db.wiz_data) на втором mysql-сервере. Причем, чтобы все новые изменения с master-таблиц, сразу же появлялись на втором сервере. Как вариант решения была выбрана репликация.

Для того, чтобы организовать репликацию между двумя MySQL-серверами необходимо:
  • На master-сервере
1) Включить binarylog, если он еще не включён и назначить master-серверу идентификатор (server-id). Для этого в my.cnf добавить и перегрузить:
log-bin=/var/db/mysql/srv011-bin.log
server-id = 1
2) Создать пользователя с правами:
GRANT REPLICATION SLAVE ON *.* TO 'slave_user'@'%' IDENTIFIED BY '';
FLUSH PRIVILEGES;

3) Заблокировать требуемые таблицы на время dump'а. Во время блокировки также необходимо посмотреть текущее состояние в binary логе
mysql> FLUSH TABLES WITH READ LOCK;
mysql> show master status;
+-------------------+----------+--------------+------------------+
File Position Binlog_Do_DB Binlog_Ignore_DB
+-------------------+----------+--------------+------------------+
srv011-bin.000813 1156293
+-------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
4) Сделать dump требуемых таблиц и снять блокировку
mysqldump -u root -p test_db > /root/test_db.db
mysql> UNLOCK TABLES;

  • На slave-сервере
1) Указать, где находится master-сервер и какие конкретно базы (таблицы) следует реплицировать. Для этого в my.cnf добавить:
max-user-connections=50
master-host=212.122.238.32
master-user=slave_user
master-password=v9X8Ds4
server-id= 2
replicate-do-db=test_db
replicate-do-table=test_db.wiz_data
replicate-do-table=test_db.wiz_main
2) Указать "отправную точку", с которой следует начать репликацию данных.
mysql> CHANGE MASTER TO MASTER_LOG_FILE='srv011-bin.000813';
Query OK, 0 rows affected (0.05 sec)

mysql> CHANGE MASTER TO MASTER_LOG_POS=1156293;
Query OK, 0 rows affected (0.05 sec)

mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 212.122.238.32
Master_User: slave_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: srv011-bin.000813
Read_Master_Log_Pos: 1156293
Relay_Log_File: relay.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: srv011-bin.000813
Slave_IO_Running: No
Slave_SQL_Running: No
Replicate_Do_DB: test_db
Replicate_Ignore_DB:
Replicate_Do_Table: test_db.wiz_main,test_db.wiz_data
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 1156293
Relay_Log_Space: 4
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
1 row in set (0.00 sec)
3) Инициировать репликацию
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 212.122.238.32
Master_User: slave_user
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: srv011-bin.000813
Read_Master_Log_Pos: 1174592
Relay_Log_File: relay.000001
Relay_Log_Pos: 18347
Relay_Master_Log_File: srv011-bin.000813
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: test_db
Replicate_Ignore_DB:
Replicate_Do_Table: test_db.wiz_main,test_db.wiz_data
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 1174592
Relay_Log_Space: 18347
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
1 row in set (0.00 sec)

Ссылки:
  1. Live Backups of MySQL Using Replication
  2. How To Set Up Database Replication In MySQL
  3. Репликация Master-Slave в MySQL
  4. Репликация mySQL — параметры и команды

2008/06/06

2>&1

Cron
  • перенаправить стандартный вывод и вывод об ошибках в файл /var/log/cron
52 6 1 * * root /root/scripts/check_disk.sh >> /var/log/cron 2>&1
  • отправить стандартный вывод и вывод об ошибках на me@domain.net
01 21 * * * root /root/scripts/check_disk.sh 2>&1 | mail -s "Disk check output" me@domain.net
Shell
  • отличие конструкций '2>&1 > file' и '> file 2>&1'
$ cat food 2>&1 > file
cat: food: No such file or directory
$ cat food > file 2>&1
$ cat file
cat: food: No such file or directory

2008/05/16

Использование Bacula для backup'а Oracle баз данных

При использовании Bacula для копирования Oracle баз данных в online-режиме (не останавливая базу, при включенной опции ARCHIVELOG) советую использовать идущую с Oracle'ом утилиту RMAN. После того, как соответствующее окружение (Flash Recovery Area, Catalog и т.п.) для RMAN'а будет настроено, на Bacula-клиенте можно использовать скрипт /u01/app/oracle/product/10.2.0/db_1/scripts/runbeforebackup:
#!/bin/bash            

if [ "$1" = "1" ]
then
LEVEL=1
elif [ "$1" = "0" ]
then
LEVEL=0
else
echo "Bad backup level"
exit 1
fi
rman catalog rcatuser/PASSWORD@RCAT target / << EOF
RUN {
BACKUP incremental level=$LEVEL as compressed backupset DATABASE INCLUDE CURRENT CONTROLFILE plus archivelog delete input;
}
EOF
На Bacula Director'е в соответствующие job'ы добавить строки:
ClientRunBeforeJob = "/bin/su - oracle -c \"/u01/app/oracle/product/10.2.0/db_1/scripts/runbeforebackup.sh LEVEL\""
где LEVEL - 0 (Full Backup) или 1 (Incremental Backup).
В зависимости от backup-политики на Вашем предприятии, конфигурируйте как часто следует использовать каждый из уровеней.

Ссылки:
  1. Oracle® Database Backup and Recovery Basics
  2. Oracle Applications DBA Blog: RMAN Archives
  3. Backing up and Restoring an Oracle database using the ControlFile as a Repository

2008/05/09

Использование Bacula для backup'а MySQL баз данных

При использовании Bacula для копирования MySQL баз данных на Bacula-клиенте (там, откуда необходимо копировать mysql-базы) необходимо создать скрипт /usr/loca/bin/mysqlhotcopyall:
#!/bin/bash
DBLIST="db1 db2 db3 mysql" # здесь необходимо указать имена баз, которые следует копировать
DBDIR=/var/lib/bacula/mysql
UP=" --user=$1 --password=$2"
LOGFILE=/var/log/backup.log
mkdir $DBDIR
for DATABASE in $DBLIST
do
mysqlhotcopy $UP $DATABASE ${DBDIR} --allowold >> ${LOGFILE}
done
Также необходимо в MySQL'е создать пользователя bacula, который будет выполнять блокировку и копирование указанных баз на время backup'а. Данному пользователю должны быть предоставлены глобальные привилегии SELECT, RELOAD, LOCK TABLES.
CREATE USER 'bacula'@'localhost' IDENTIFIED BY 'PASSWORD';
GRANT SELECT, RELOAD, LOCK TABLES ON *.* TO 'bacula'@'localhost' IDENTIFIED BY 'PASSWORD';
На Bacula Director'е в соответствующий job добавить строки:
ClientRunBeforeJob = "/usr/local/bin/mysqlhotcopyall bacula PASSWORD"
ClientRunAfterJob = "/bin/rm -rf /var/lib/bacula/mysql"
Благодаря указанным параметрам перед backup'ом с помощью утилиты mysqlhotcopy указанные базы будут скопированы в каталог /var/lib/bacula/mysql. После backup'а содержимое данного каталога будет очищено.

Поправка - данное решение работает только для таблиц типа MyISAM и ARCHIVE, таблицы INNODB следует копировать как минимум вручную, либо mysqldump'ом.

2008/05/07

PPTP VPN-клиент для Linux Ubuntu

Для того, чтобы установить VPN-туннель с удаленной сетью и работать в ней как в обычной локальной сети достаточно установить пакет pptp-linux:
sudo apt-get install pptp-linux
После этого достаточно добавить логин/пароль в файл /etc/ppp/chap-secrets в формате:
$DOMAIN\\$USERNAME PPTP $PASSWORD *
А также создать файл /etc/ppp/peers/$TUNNEL примерно такого содержания:
pty "pptp $SERVER --nolaunchpppd"
name $DOMAIN\\$USERNAME
remotename PPTP
require-mppe-128
file /etc/ppp/options.pptp
ipparam $TUNNEL
Для установления соединения:
sudo pon $TUNNEL
Для разрыва соединения:
sudo poff $TUNNEL
В заключении необходимо прописать дополнительный маршрут для доступа к удаленной сети (например к сети 192.168.1.0/24 через маршрутизатор 192.168.1.1):
sudo route add -net 192.168.1.0 netmask 255.255.255.0 gw 192.168.1.1
Чтобы это хозяйство использовать ежедневно, желательно всё оформить в виде скрипта /etc/ppp/ip-up.d/$TUNNEL, тогда каждый раз при успешном установлении соединении будет выполняться данный скрипт.
cat > /etc/ppp/ip-up.d/$TUNNEL
#!/bin/bash

route add -net 192.168.1.0 netmask 255.255.255.0 gw 192.168.1.1
[Crtl+d]
chmod +x /etc/ppp/ip-up.d/$TUNNEL

Ссылки:
  1. PPTP Client

2008/05/02

Mailhub-сервер на базе Exim

Второй раз столкнулся с задачей конфигурирования основного mx-сервера и так уж получилось, что это вновь оказался Exim (дистрибутив Debian Etch 4.0r3). Решил уже всё дотошно описать, чтобы впоследствии можно было с легкостью восстановить требуемую конфигурацию. Данное описание содержит комментарии только к внесенным мною изменениям, стандартные конфигурационные параметры я не описываю и не указываю.

В моей конфигурации основной mx-сервер выступает в роли "привратника", который принимает из Internet всю почту и рассылает далее по внутренним smtp-серверам, поэтому упор делается на отброс нежелательной корреспонденции (spam'a).
Комментарии приветствуются :)

Основной конфигурационный файл Exim для "пересборки" выглядит примерно так:
# cat /etc/exim4/update-exim4.conf
dc_eximconfig_configtype='internet'
dc_other_hostnames='mail.mycompany.com'
dc_local_interfaces=''
dc_readhost=''
dc_relay_domains='mycompany.com:mycompany.eu:mycompany.net'
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='mail_spool'
Для удобства внесения изменений в конфигурацию я использую мини-скрипт rebuild
cat > /etc/exim4/rebuild
#!/bin/bash
update-exim4.conf
/etc/init.d/exim4 restart
[Ctrl+d]
chmod +x /etc/exim4/rebuild
В базовой комплектации Debian Exim идёт в "лёгкой" сборке, но для того, чтобы использовать возможность фильтровать вирусы с помощью ClamAv необходим heavy-пакет.
# apt-get install exim4-daemon-heavy
Антивирус прикручивается к Exim'у просто, но для его установки следут использовать volatile-репозитарий. Перед установкой пакета clamav-daemon, следует добавить в /etc/apt/sources.list соответствующую строку и обновить apt-get.
# cat >> /etc/apt/sources.list
deb http://volatile.debian.org/debian-volatile etch/volatile main contrib non-free
[Ctrl+d]
# apt-get update
# apt-get install clamav-daemon
Чтобы clamav-пользователь имел возможность читать и писать в каталог /var/spool/exim4/scan, его необходимо добавить в группу Debian-exim
# usermod -G Debian-exim clamav
Чтобы научить Exim отдавать на проверку stmp-траффик, необходимо добавить строку
av_scanner = clamd:/var/run/clamav/clamd.ctl
в main-отдел конфигурации Exim'а (каталог /etc/exim4/conf.d/main/).
Затем добавить в acl-отдел конфигурации строки
deny message = This message contains a virus: ($malware_name) please scan your system.
malware = */defer_ok
Далее начинается самое ответственное - конфигурация Exim'а для первичной фильтрации спама:
/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs
  • CHECK_RCPT_IP_DNSBLS = bl.spamcop.net
/etc/exim4/conf.d/main/02_exim4-config_options
  • smtp_banner = Welcome to the Moon $tod_full - изменяем banner
  • rfc1413_query_timeout = 0s - отключаем Ident-запросы, чтобы избавиться от задержки при подключению к smtp-сервису
  • helo_allow_chars = _ - чтобы разрешить в HELO/EHLO символ _
/etc/exim4/conf.d/acl/30_exim4-config_check_mail
acl_check_mail:
deny
message = no HELO given before MAIL command
condition = ${if def:sender_helo_name {no}{yes}}

accept
/etc/exim4/conf.d/acl/30_exim4-config_check_rcptd
acl_check_rcpt:
accept
hosts = :
# описание этого условия можно найти тут
drop message = Forged IP in HELO.
log_message = HELO is our IP
condition = ${if eq {${lookup {$sender_helo_name} \
lsearch{/etc/exim4/reject_helo} \
{yes}{no}}}{${if !eq {$sender_host_address}{127.0.0.1}{yes}{no}}}{yes}{no}}

accept
.ifndef CHECK_RCPT_POSTMASTER
local_parts = postmaster
.else
local_parts = CHECK_RCPT_POSTMASTER
.endif
domains = +local_domains : +relay_to_domains

accept
hosts = +relay_from_hosts
control = submission/sender_retain

accept
authenticated = *
control = submission/sender_retain

accept
domains = +local_domains
endpass
message = unknown user
verify = recipient

# Иногда бывает удобно воспользоваться данным условием, чтобы заблокировать отправителей,
# указав их в файле /etc/exim4/local_sender_blacklist
deny
message = sender envelope address $sender_address is locally blacklisted here. If you think this is wrong, get in touch with postmaster
!acl = acl_whitelist_local_deny
senders = ${if exists{CONFDIR/local_sender_blacklist}\
{CONFDIR/local_sender_blacklist}\
{}}

# То же самое, только касательно хостов-отправителей
deny
message = sender IP address $sender_host_address is locally blacklisted here. If you think this is wrong, get in touch with postmaster
!acl = acl_whitelist_local_deny
hosts = ${if exists{CONFDIR/local_host_blacklist}\
{CONFDIR/local_host_blacklist}\
{}}

# Полезное правило, но с ним нужно быть осторожным. На основном mx-сервере я оставляю его
# в warn-режиме, чтобы вдруг не заблокировать неродиво настроенных серверов, а вот на вторичных всегда выставляю в deny.
warn
message = X-Host-Lookup-Failed: Reverse DNS lookup failed for $sender_host_address (${if eq{$host_lookup_failed}{1}{failed}{deferred}})
condition = ${if and{{def:sender_host_address}{!def:sender_host_name}}\
{yes}{no}}

# Проверка по DNSBLS. Почта отбрасывается, если хост находится сразу в трёх DNS Black листах:
# bl.spamcop.net, xbl.spamhaus.org, ctl.abuseat.org.
.ifdef CHECK_RCPT_IP_DNSBLS
drop
message = X-Warning: $sender_host_address is listed at DNSLists (bl.spamcop.net,xbl.spamhaus.org,cbl.abuseat.org) and out system thinks your message is spam.
log_message = $sender_host_address is listed at DNSLists
dnslists = CHECK_RCPT_IP_DNSBLS
dnslists = xbl.spamhaus.org
dnslists = cbl.abuseat.org
.endif

# Проверка отправителя. Callout означает, что данных почтовый сервер, для того чтобы проверить
# существование указанного отправителя, инициирует попытку отправить письмо указанному отправителю.
# Опция defer_ok предостерегает от отбрасывания серверов с greylisting'ом, либо с серверов,
# на которые временно невозможно присоединиться
deny
message = Sender verification failed
!acl = acl_whitelist_local_deny
domains = +relay_to_domains
!verify = sender/callout=30s,defer_ok

# Обязательная проверка получателя для внутренних доменов (relay_to_domains) callout'ом.
accept
domains = +relay_to_domains
endpass
message = unknown user
verify = recipient/callout

# Если все предыдущие условия не подходят, отбрасываем письмо.
deny
message = relay not permitted
А для того, чтобы данный основной mx-сервер рассылал почту конкретным внутренним серверам, достаточно добавить файл /etc/exim4/hubbed_hosts примерно такого содержания:
mycompany.com: 192.168.1.2
mycompany.eu: 192.168.1.2
mycompany.net: 192.168.1.3
Ссылки:
  1. Specification of the Exim Mail Transfer Agent

2008/03/27

X11Forwarding после su

Чтобы X11Forwarding работал на CentOS/RHEL, в системе должен быть установлен пакет xorg-x11-xauth, в комплекте которого идёт утилита xauth.
Статья Getting X11 forwarding through ssh working after running su объясняет, что необходимо выполнить, чтобы X11Forwarding работал после переключения на другого пользователя (su -).

[home]$ ssh -X roman@work
[work]$ id
uid=1000(roman) gid=2000(users)
[work]$ xauth list
work/unix:12 MIT-MAGIC-COOKIE-1 42e71ddd7a5bdf635e5d4d52eafa3097
work/unix:10 MIT-MAGIC-COOKIE-1 74c837450ad8d9720a914fa00b0b8eab
[work]$ su -
password:
[work]# su - oracle
[work]$ id
uid=500(oracle) gid=501(oinstall) groups=500(dba),501(oinstall)
[work]$ xauth add work/unix:10 MIT-MAGIC-COOKIE-1 74c837450ad8d9720a914fa00b0b8eab
[work]$ xclock

2008/03/13

Аутентификация Linux-сервера через Windows AD Domain Controller

Данное оипсание применялось на дистрибутиве CentOS/RHEL 5 в связке с Windows 2003 Server.
Для начала установим необходимые для работы пакеты
# yum install -y krb5-workstation samba-common

Подправим файл /etc/hosts, чтобы он имел примерно такой вид:
# vi /etc/hosts
127.0.0.1 localhost.localdomain localhost
10.10.2.91 vm01.organization.local vm01
10.10.2.1 windows.organization.local windows

Теперь настроим Керберос для добавления linux-сервера в windows-домен
# vi /etc/krb5.conf
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]javascript:void(0)
default_realm = ORGANIZATION.LOCAL
dns_lookup_realm = false
dns_lookup_kdc = false
ticket_lifetime = 24h
forwardable = yes

[realms]
ORGANIZATION.LOCAL = {
kdc = windows.organization.local:88
admin_server = windows.organization.local:749
default_domain = organization.local
}

[domain_realm]
.organization.local = ORGANIZATION.LOCAL
organization.local = ORGANIZATION.LOCAL

[kdc]
profile = /var/kerberos/krb5kdc/kdc.conf

[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}

# kinit administrator@ORGANIZATION.LOCAL
Password for administrator@ORGANIZATION.LOCAL:

Минимальная настройка samba
[root@vm01 ~]#vi /etc/samba/smb.conf
[global]
workgroup = ORGANIZATIONAL
netbios name = VM01
server string = VM01 Samba Server
security = ads
encrypt passwords = yes
realm = ORGANIZATIONAL.LOCAL
password server = windows.organization.local
winbind enum users = yes
winbind enum groups = yes
winbind cache time = 1800
winbind use default domain = yes
winbind refresh tickets = yes
idmap uid = 10000-20000
idmap gid = 10000-20000
template shell = /bin/bash
template homedir = /home/%U
printing =
load printers = no

Входим в домен
# net ads join -U administrator
administrator's password:
Using short domain name -- ORGANIZATION
Joined 'VM01' to realm 'ORGANIZATION.LOCAL'

Добавить описание pam_winbind.so модуля для аутентификации в системе. Модуль pam_mkhomedir.so для автоматического создания домашней директории при первом присоединении пользователя.
vim /etc/pam.d/system-auth
auth required pam_env.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 500 quiet
auth sufficient pam_winbind.so use_first_pass
auth required pam_deny.so

account required pam_unix.so
account sufficient pam_succeed_if.so uid < 500 quiet
account [default=bad success=ok user_unknown=ignore] pam_winbind.so
account required pam_permit.so

password requisite pam_cracklib.so try_first_pass retry=3
password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok
password sufficient pam_winbind.so use_authtok
password required pam_deny.so

session optional pam_keyinit.so revoke
session required pam_limits.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
session required pam_mkhomedir.so skel=/etc/skel/ umask=0022

Настраиваем nsswitch, для того чтобы он мог использовать данные (о пользователях и групппах) AD с помощью winbind-сервиса и стартуем сервис
vim /etc/nsswitch.conf
passwd: files winbind
shadow: files
group: files winbind

/etc/init.d/winbind start
Starting Winbind services: [ OK ]
chkconfig winbind on

Ограничиваем доступ к ssh-сервису
vim /etc/ssh/sshd_config
PermitRootLogin no
AllowGroups admins

/etc/init.d/sshd restart
Stopping sshd: [ OK ]
Starting sshd: [ OK ]