В предыдущей статьи я описал общий концепт для полного шифрования ОС Linux семейства rpm,deb (предыдущая инструкция тестировалась для Ubuntu, Debian, Centos Red Hat) при установке. Теперь, в продолжение статьи напишу инструкцию по удаленной разлочки зашифрованных luks томов под дистрибутивом Centos 6.4. Она так же подойдет и для Red Hat.
В отличие от настройки удаленной разлочки для deb-подобных систем, в Centos для обновления и создания initramfs используется – dracut . И для удаленной разлочки нам понадобиться писать специальный модуль под него.
Для начала нужно установить нужные пакеты. Так как пакета dropbear нету в стандартных репозиториях – нужно подключить свежий epel.
[root@slave ~]# wget http://mirror-fpt-telecom.fpt.net/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm [root@slave ~]# rpm -ivh epel-release-6-8.noarch.rpm [root@slave ~]# yum -y update [root@slave ~]# yum -y install dropbear cryptsetup busybox
Теперь нужно добавить dracut модуль. Если ваш сервер имеет статический IP, тогда установка и настройка модуля будет простой. Суть состоит в том, чтобы при начальной загрузке установить драйвер сети и статически вбить настройки интерфейса и добавить юзера для удаленной разлочки. Такая инструкция уже есть, можно почитать здесь . Я же хочу описать, как настроить раздачу адреса по dhcp. Для этого нам нужно писать скрипт, который будет имитировать dhcp клиент (в initramfs простой вызов команды dhclient не поднимет ваш интерфейс).
Приступаем.
[root@slave ~]# mkdir /usr/share/dracut/modules.d/30unlock [root@slave ~]# cd /usr/share/dracut/modules.d/30unlock
Добавляем скрипт проверки и загрузку сетевого драйвера
[root@slave 30unlock]# cat check #!/bin/sh exit 0 [root@slave 30unlock]# cat installkernel #!/bin/bash instmods pcnet32
В данном случаи имя драйвера сети — pcnet32. Чтобы узнать который у вас драйвер, можно использовать утилиту ethtool.
[root@slave 30unlock]# ethtool -i eth0 | grep driver driver: pcnet32
Далее добавляем пользователя, который будет делать разлочку и создаем ключ для подключения через dropbear.
[root@slave 30unlock]# echo 'root:x:0:0:root:/home/root:/bin/bash' > passwd [root@slave 30unlock]# echo '/bin/bash' > shells [root@slave 30unlock]# echo 'export PATH=/sbin:/usr/sbin:$PATH' >.profile [root@slave 30unlock]# dropbearkey -t rsa -f dropbear_rsa_host_key Will output 1024 bit rsa secret key to 'dropbear_rsa_host_key' Generating key, this may take a while... Public key portion is: ssh-rsa AAAAB3NzaC1yc2EAASAADAQABAAAAgwC72SlhA7crmgxq+QJKxYa9NIxFiHFkw4VD65dOyeDAgQxW7LTlfc8JTMF3st+l2Kih/Z8Uyav8csG/Ti0YwaHyIbFG7MgO+WXehyFcuF+VGQFBjJSC2tuHFzJFtCDTVAeORM0aLMOWwBrVspXY/GtfQa0SiL/JkejS5z root@slave Fingerprint: md5 4c:fa:6e:68:93:36:01:1a:2b:46:d7:7a:58:21:80:73 [root@slave 30unlock]# dropbearconvert dropbear openssh dropbear_rsa_host_key dropbear.key Key is a RSA key Wrote key to 'dropbear.key' [root@slave 30unlock]# dropbearkey -y -f dropbear_rsa_host_key | grep "^ssh-rsa " > id_rsa.pub [root@slave 30unlock]# cat id_rsa.pub >> authorized_keys
Добавляем файл служебной информации.
[root@slave 30unlock]# cat nsswitch.conf passwd: files shadow: files group: files initgroups: files hosts: files dns bootparams: files ethers: files netmasks: files networks: files protocols: files rpc: files services: files automount: files aliases: files
Добавляем скрипт разлочки
[root@slave 30unlock]# cat unlock #!/bin/bash if [ -f /etc/crypttab ] ; then sed '/^$/d;/^#/d' /etc/crypttab > /tmp/crypttab n=1 line="`sed -n "$n"p /tmp/crypttab`" while [ -n "$line" ]; do name="`echo $line|awk '{ print $1 }'`" dev="`echo $line|awk '{ print $2 }'`" key="`echo $line|awk '{ print $3 }'`" if [ "$key" = "none" ]; then luksname="$name" if [ "${dev%%=*}" = "UUID" ]; then device="`blkid -t $dev|cut -d: -f1`" else device=$dev fi echo "Password [$device ($luksname)]:" while :; do cryptsetup luksOpen $device $luksname && break done fi n=$((n+1)) line="`sed -n "$n"p /tmp/crypttab`" done sed -i /cryptsetup/c\ true /pre-pivot/* [ "$1" = "-noexit" ] && exit 0 killall plymouth killall cryptroot-ask fi exit 0
Скрипт для установки модуля сети и поднятия dropbear.
[root@slave 30unlock]# cat ssh.sh #!/bin/sh /sbin/modprobe pcnet32 /sbin/ifup eth0 /usr/sbin/dropbear -E -m -p 2222 -a -K 600 echo "dropbear is starting" ping -c 4 8.8.8.8
Добавляем основной скрипт установки модуля
[root@slave 30unlock]# cat install ARCH=$(uname -m) for dir in /usr/$LIBDIR/tls/$ARCH/ /usr/$LIBDIR/tls/ /usr/$LIBDIR/$ARCH/ /usr/$LIBDIR/ /$LIBDIR/; do for i in $(ls $dir/libnss_dns.so.* $dir/libnss_mdns4_minimal.so.* 2>/dev/null); do dracut_install $i done done cat /etc/shadow|grep root > ${moddir}/shadow inst_dir "/etc/dropbear" inst "${moddir}/dropbear_rsa_host_key" "/etc/dropbear/dropbear_rsa_host_key" inst_dir "/home" inst_dir "/home/root" inst_dir "/home/root/.ssh" inst "${moddir}/.profile" "/home/root" [ -s "${moddir}/authorized_keys" ] && inst "${moddir}/authorized_keys" "/home/root/.ssh/authorized_keys" inst "/etc/localtime" inst "${moddir}/nsswitch.conf" "/etc/nsswitch.conf" inst "/etc/resolv.conf" inst "/etc/host.conf" inst "/etc/hosts" inst "${moddir}/shadow" "/etc/shadow" inst "${moddir}/passwd" "/etc/passwd" inst "${moddir}/shells" "/etc/shells" inst "${moddir}/unlock" "/bin/unlock" inst "${moddir}/ifup" "/sbin/ifup" inst "${moddir}/netroot" "/sbin/netroot" inst "${moddir}/dhclient-script" "/sbin/dhclient-script" inst "${moddir}/dhclient.conf" "/etc/dhclient.conf" inst_hook pre-udev 01 "${moddir}/ssh.sh" dracut_install ip dhclient brctl arping tr sed awk dropbear ifconfig route blkid cut killall true mkdir dracut_install -o ps find grep less cat tac head tail false rmdir rm touch vi ping ssh scp lsmod
Добавляем настройки dhcp-клиента.
[root@slave 30unlock]# cat dhclient.conf request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, domain-search, host-name, root-path, interface-mtu;
Добавляем скрипт для поднятия и настройки dhcp
[root@slave 30unlock]# cat dhclient-script #!/bin/sh setup_interface() { ip=$new_ip_address mtu=$new_interface_mtu mask=$new_subnet_mask bcast=$new_broadcast_address gw=${new_routers%%,*} domain=$new_domain_name search=$(printf "$new_domain_search") namesrv=$new_domain_name_servers hostname=$new_host_name [ -f /tmp/net.$netif.override ] && . /tmp/net.$netif.override # Taken from debian dhclient-script: # The 576 MTU is only used for X.25 and dialup connections # where the admin wants low latency. Such a low MTU can cause # problems with UDP traffic, among other things. As such, # disallow MTUs from 576 and below by default, so that broken # MTUs are ignored, but higher stuff is allowed (1492, 1500, etc). if [ -n "$mtu" ] && [ $mtu -gt 576 ] ; then if ! ip link set $netif mtu $mtu ; then ip link set $netif down ip link set $netif mtu $mtu ip link set $netif up wait_for_if_up $netif fi fi ip addr add $ip${mask:+/$mask} ${bcast:+broadcast $bcast} dev $netif > /tmp/net.$netif.up ip addr add $ip${mask:+/$mask} ${bcast:+broadcast $bcast} dev $netif [ -n "$gw" ] && ip route add default via $gw dev $netif [ -n "$gw" ] && echo ip route add default via $gw dev $netif > /tmp/net.$netif.gw [ -n "${search}${domain}" ] && echo "search $search $domain" > /tmp/net.$netif.resolv.conf if [ -n "$namesrv" ] ; then for s in $namesrv; do echo nameserver $s done fi >> /tmp/net.$netif.resolv.conf [ -n "$hostname" ] && echo "echo $hostname > /proc/sys/kernel/hostname" > /tmp/net.$netif.hostname } PATH=$PATH:/sbin:/usr/sbin export PS4="dhclient.$interface.$$ + " exec >>/dev/initlog.pipe 2>>/dev/initlog.pipe . /lib/dracut-lib.sh # We already need a set netif here netif=$interface case $reason in PREINIT) echo "dhcp: PREINIT $netif up" ip link set $netif up wait_for_if_up $netif ;; BOUND) echo "dhcp: BOND setting $netif" if ! arping -q -D -c 2 -I $netif $new_ip_address ; then warn "Duplicate address detected for $new_ip_address while doing dhcp. retrying" exit 1 fi setup_interface set | while read line; do [ "${line#new_}" = "$line" ] && continue echo "$line" done >/tmp/dhclient.$netif.dhcpopts echo online > /sys/class/net/$netif/uevent /sbin/initqueue --onetime --name netroot-$netif /sbin/netroot $netif ;; *) echo "dhcp: $reason";; esac exit 0
Добавляем скрипт для настройки параметров сетевого интерфейса
[root@slave 30unlock]# cat ifup #!/bin/sh # # We don't need to check for ip= errors here, that is handled by the # cmdline parser script # PATH=$PATH:/sbin:/usr/sbin . /lib/dracut-lib.sh # Run dhclient do_dhcp() { # /sbin/dhclient-script will mark the netif up and generate the online # event for nfsroot # XXX add -V vendor class and option parsing per kernel #echo "Starting dhcp for interface $netif" dhclient "$@" -1 -q -cf /etc/dhclient.conf -pf /tmp/dhclient.$netif.pid -lf /tmp/dhclient.$netif.lease $netif # increase wait time for DHCP echo '[ $(($RDRETRY-$main_loop)) -lt 720 ] && RDRETRY=$(($main_loop+720)); rm -f "$job"' > /initqueue/rd_retry_inc_dhclient.$netif.sh #|| echo "dhcp failed" } load_ipv6() { modprobe ipv6 i=0 while [ ! -d /proc/sys/net/ipv6 ]; do i=$(($i+1)) [ $i -gt 10 ] && break sleep 0.1 done } do_ipv6auto() { load_ipv6 { echo 0 > /proc/sys/net/ipv6/conf/$netif/forwarding echo 1 > /proc/sys/net/ipv6/conf/$netif/accept_ra echo 1 > /proc/sys/net/ipv6/conf/$netif/accept_redirects ip link set $netif up wait_for_if_up $netif } > /tmp/net.$netif.up [ -n "$hostname" ] && echo "echo $hostname > /proc/sys/kernel/hostname" > /tmp/net.$netif.hostname namesrv=$(getargs nameserver) if [ -n "$namesrv" ] ; then for s in $namesrv; do echo nameserver $s done fi >> /tmp/net.$netif.resolv.conf echo online > /sys/class/net/$netif/uevent /sbin/initqueue --onetime --name netroot-$netif /sbin/netroot $netif } # Handle static ip configuration do_static() { strstr $ip '*:*:*' && load_ipv6 { ip link set $netif up wait_for_if_up $netif # do not flush addr for ipv6 #strstr $ip '*:*:*' || \ # ip addr flush dev $netif ip addr add $ip/$mask brd + dev $netif } > /tmp/net.$netif.up [ -n "$gw" ] && echo ip route add default via $gw dev $netif > /tmp/net.$netif.gw [ -n "$hostname" ] && echo "echo $hostname > /proc/sys/kernel/hostname" > /tmp/net.$netif.hostname namesrv=$(getargs nameserver) if [ -n "$namesrv" ] ; then for s in $namesrv; do echo nameserver $s done fi >> /tmp/net.$netif.resolv.conf echo online > /sys/class/net/$netif/uevent /sbin/initqueue --onetime --name netroot-$netif /sbin/netroot $netif } PATH=$PATH:/sbin:/usr/sbin export PS4="ifup.$1.$$ + " exec >>/dev/initlog.pipe 2>>/dev/initlog.pipe . /lib/dracut-lib.sh # Huh? No $1? [ -z "$1" ] && exit 1 # $netif reads easier than $1 netif=$1 # bridge this interface? if [ -e /tmp/bridge.info ]; then . /tmp/bridge.info if [ "$netif" = "$ethname" ]; then netif="$bridgename" fi fi if [ -e /tmp/vlan.info ]; then . /tmp/vlan.info if [ "$netif" = "$phydevice" ]; then if [ "$netif" = "$bondname" ] && [ -n "$DO_BOND_SETUP" ] ; then : # We need to really setup bond (recursive call) else netif="$vlanname" fi fi fi # bail immediately if the interface is already up # or we don't need the network [ -f "/tmp/net.$netif.up" ] && exit 0 [ -f "/tmp/root.info" ] || exit 0 . /tmp/root.info #[ -z "$netroot" ] && exit 0 # loopback is always handled the same way if [ "$netif" = "lo" ] ; then ip link set lo up ip addr add 127.0.0.1/8 dev lo >/tmp/net.$netif.up exit 0 fi # XXX need error handling like dhclient-script # start bridge if necessary if [ "$netif" = "$bridgename" ] && [ ! -e /tmp/net.$bridgename.up ]; then ip link set $ethname up wait_for_if_up $ethname # Create bridge and add eth to bridge brctl addbr $bridgename brctl setfd $bridgename 0 brctl addif $bridgename $ethname fi get_vid() { case "$1" in vlan*) return ${1#vlan} ;; *.*) return ${1##*.} ;; esac } if [ "$netif" = "$vlanname" ] && [ ! -e /tmp/net.$vlanname.up ]; then modprobe 8021q ip link set "$phydevice" up wait_for_if_up "$phydevice" ip link add dev "$vlanname" link "$phydevice" type vlan id "$(get_vid $vlanname; echo $?)" fi # No ip lines default to dhcp ip=$(getarg ip) if [ -z "$ip" ]; then if [ "$netroot" = "dhcp6" ]; then do_dhcp -6 else do_dhcp -4 fi fi # Specific configuration, spin through the kernel command line # looking for ip= lines for p in $(getargs ip=); do ip_to_var $p # skip ibft [ "$autoconf" = "ibft" ] && continue # If this option isn't directed at our interface, skip it [ -n "$dev" ] && [ "$dev" != "$netif" ] && continue # Store config for later use for i in ip srv gw mask hostname; do eval '[ "$'$i'" ] && echo '$i'="$'$i'"' done > /tmp/net.$netif.override OLDIFS="$IFS" IFS=, set -- $autoconf IFS="$OLDIFS" for autoconf in "$@"; do case $autoconf in dhcp|on|any) do_dhcp -4 ;; dhcp6) do_dhcp -6 ;; auto6) do_ipv6auto ;; *) do_static ;; esac done break done exit 0
Добавляем скрипт для завершения работы dhcp после разлочки.
[root@slave 30unlock]# cat kill-dhclient.sh #!/bin/sh for f in /tmp/dhclient.*.pid; do [ -e $f ] || continue read PID < $f; kill $PID; done
Теперь делаем нужные скрипты – исполняемыми
[root@slave 30unlock]# chmod a+x check install installkernel ssh.sh unlock dhclient-script ifup [root@slave 30unlock]# ls authorized_keys dhclient.conf dropbear.key id_rsa.pub install kill-dhclient.sh passwd shells check dhclient-script dropbear_rsa_host_key ifup installkernel nsswitch.conf ssh.sh unlock
Теперь нужно обновить initramfs для добавления в него нашего модуля. И проверить, подхватился ли наш модуль
[root@slave 30unlock]# dracut -f [root@slave 30unlock]# lsinitrd /boot/initramfs-2.6.32-279.el6.x86_64.img | grep ssh.sh -rwxr-xr-x 1 root root 140 Sep 30 20:57 pre-udev/01ssh.sh
Все действия идут по аналогии с удаленной разлочкой Ubuntu.
Теперь копируем ключ, перезагружаемся и проверяем.
root@ubuntu:~# ssh -o "UserKnownHostsFile=~/.ssh/known_hosts.initramfs" -i dropbear.key root@192.168.1.108 -p 2222 [380] Oct 01 14:17:41 lastlog_perform_login: Couldn't stat /var/log/lastlog: No such file or directory [380] Oct 01 14:17:41 lastlog_openseek: /var/log/lastlog is not a file or directory! -bash-4.1# unlock Password [/dev/sda2 (luks-2952e504-569b-438d-b43c-cdaa6ddcfd3a)]: Enter passphrase for /dev/sda2: -bash-4.1#
Если возникли проблемы – пишите, помогу чем смогу.