FreeBSD Installation with ZFS- on-root over GELI! 이성곤 version 0.9.2 0. Prerequisite! FreeBSD 설치를 위해 준비한 시스템 사양은 아래와 같다. 1. HP ProLiant N54L Microserver 2. Samsung PC12800 4GB * 2 3. Seagate Barracuda 3TB (ST3000DM001) * 6 N54L은 기본적으로 하드디스크 4개에 ODD 지원용 슬롯하나를 가지고 있고, ESATA 1 port를 지원하고 있다. 이 6개를 모두 활용하려면 hack firmware를 설치해서 AHCI로 인식시켜야한다. 여기서는 범위를 넘어서기에 생 략한다. 자세한 내용은 HP ProLiant N40L MicroServer Build and BIOS Modification Revisited (http:// homeservershow.com/hp-proliant-n40l-microserver-build-and-bios-modification-revisited.html)를 참 조하기 바란다. 메모리는 기본적으로 Unbuffered ECC를 지원하고 있으나, ECC 지원없는 메모리를 설치해도 작동가능하다. 다만 장기적 안정성은 보장할 수 없다. 지원 스펙은 PC10600으로 되어 있다. 1. 설치 전략! GELI는 AES256-XTS(또는 AES256-CBC)를 기반으로 물리레벨의 partition을 암호화 해주는 FreeBSD 자 체기능이다. 암호화하는 데에는 key와 passphrase가 함께 사용가능하다. 물론 암호화된 partition를 인식하기 위해서 암호화되기 전의 key와 passphrase를 입력해주어야 가능하다. 즉, GELI를 구동시켜 암호화된 partition 을 인식시켜줄 별도의 부팅 시스템이 필요하다는 것을 의미한다. 이를 위한 전략으로는 대표적으로 두가지 방식이 있다. 1. 하나의 디스크내에 논리적으로 나뉘는 두 영역을 마련하고 부팅용 영역에 키를 담아두는 방식

예를들어 애플의 FileVault 2가 이런 방식과 가깝다고 할 수 있다. FileValue 2는 capsule 방식이라고 할 수 있다. 내부에 contents를 담아둔 영역이 있고, 키를 입력하면 contents에 접근 가능하도록 한다. FreeBSD 에서는 이런 capsule 방식은 지원하지 않고, 별도로 분리된 partition을 마련해서 적용이 가능하다. 장점은 별도의 디스크가 필요없고, 부팅 또는 연결시 passphrase만 입력하는 것으로 사용이 가능하다는 점 을 들 수 있으나, 단점은 디스크 자체에 키가 들어있는 만큼 해킹의 여지가 있다는 점이다. 2. 별도의 부팅용 디스크에 키를 담아두는 방식

USB disk와 같은 별도의 디스크를 마련하여 부팅때에만 사용하고 부팅후에는 바로 빼내어 따로 보관하는 방 식을 말한다. 부팅할 때마다 매번 연결해야하고, 커널 업데이트 등 부팅과 관련한 변경이 있을 때, 부팅 디스 크를 다시 설정해야하는 불편이 따르지만, 키 파일을 별도로 관리함으로써 강력한 보안성능을 얻을 수 있다. 부팅용 디스크는 부팅할 때, 커널의 구동과 root의 연결에만 관여하고 시스템에 mount 되지 않기 때문에 부 팅후에는 시스템에서 빼내어도 상관이 없다. 여기서는 별도의 부팅용 디스크를 사용하여 부팅하는 전략에 대해서만 자세히 다루도록 하겠다. 다만, 두 영역을 만드는 방식은 파티션 만들어서 부팅용 파티션을 제공하는 것외에는 다른 점이 없으므로 응용에 큰 어려움이 없을 것이다. FreeBSD 10-BETA3를 기준으로 하자면 /boot 영역은 500MB가 조금 안되는 수준이다. 따라서 극단적으로는 500MB USB disk로도 사용이 가능하지만, 장차 업데이트될 것을 고려해서 적어도 1GB 이상의 USB disk를 사용할 것을 권장한다. 참고로 파티션 방식을 쓸 경우에도 부팅용 파티션에 2GB 정도는 할당할 것을 권장한다. 위의 시스템에서 여섯개의 HDD는 ada0~5로 인식되며, USB disk는 da0로 인식된다. 2. GELI 설정! FreeBSD 10-BETA3 LiveCD는 설치를 지원하거나 LiveCD 자체로 구동되는 기능을 제공하고 있다. 다만 설 치과정에서 GELI를 직접 지원하고 있지 않기에, 대부분을 수동으로 설치해야한다. LiveCD로 부팅한 후 shell을 구동시키면, exit시 원래의 화면으로 돌아간다는 메시지와 shell이 실행되어 있다. 이때 root는 CD의 root가 마 운트되어 있다. 먼저 3TB HDD를 인식시키기 위해 GPT scheme을 설정하고, boot partition이 필요없고, swap도 내에 만들 것이므로 전 영역을 -zfs partition으로 설정한다. 기본적으로 partition은 device에 p1, p2와 같이 p와 partition 번호가 부여된 block device로 생성된다. 빠른 참조를 위해 label을 부여할 수 있다. 부여한 label 은 /dev/gpt에 생성되며, 리부팅시에는 생성되지 않는다. 아래의 예제에서는 /dev/ada0p1이 생성되며, /dev/ gpt/disk0이라는 label이 부여된다. gpart -s GPT ada0! gpart -t freebsd-zfs -l disk0 ada0 Partition 암호화를 위해 geli로 초기화한다. 암호화를 위한 key와 passphrase는 각각 -b, -K로 설정 가능하다. 먼저 key 파일은 /dev/urandom을 이용해서 128kB 크기로 생성한다. 그 다음 geli init 명령어에서 생성된 key 파일과 keyboard로 입력하는 passphrase로 디스크를 초기화한다. Passphrase (-b) 또는 key (-K)에 의한 암 호화가 필요없을 경우에는 각각의 option을 사용하지 않으면 된다. mkdir -p /var/tmp/keys! dd if=/dev/urandom of=/var/tmp/keys/ada0p1.key bs=128k count=1! geli init -b -K /var/tmp/keys/ada0p1.key -s 4096 -l 256 /dev/gpt/ disk0 암호화된 partition에 접근하기 위해서 geli attach 명령어를 사용한다. Passphrase는 초기화때 등록했다면 attach하는 과정에서 입력을 요구하게 된다. 부팅시에는 attach 과정이 자동으로 실행된다. 물론 passphrase는 키보드로 입력해야만 한다. geli attach -k /var/tmp/keys/ada0p1.key /dev/gpt/disk0 다음과 같은 메시지가 뜨는 경우면 정상적으로 접근이 가능하다. cryptosoft0: on motherboard! aesni0: on motherboard! GEOM_ELI: Device gpt/disk0.eli created.! GEOM_ELI: Encryption: AES-XTS 256! GEOM_ELI: Crypto: hardware 이 과정 전체를 하나의 스크립트로 표현하면 아래와 같다. 6개의 HDD를 동시에 실행시키는 쉘스크립트로, 실행 결과 /dev/gpt/disk[012345].eli device가 생성된다. mkdir -p /var/tmp/keys! for D in 0 1 2 3 4 5; do ! gpart -s GPT ada${D}! gpart -t freebsd-zfs -l disk${D} ada${D}! dd if=/dev/urandom of=/var/tmp/keys/ada${D}p1.key bs=128k count=1! geli init -b -K /var/tmp/keys/ada${D}p1.key -s 4096 -l 256 /dev/ gpt/disk${D}! geli attach -k /var/tmp/keys/ada${D}p1.key /dev/gpt/disk${D}! done 3. ZFS partitioning & Installation! 우선 생성된 eli device를 이용해서 ZFS pool을 생성한다. 이 시스템의 경우 6개의 HDD를 raidz1 (RAID5와 유사)으로 묶어서 사용하려고 한다. 속력향상을 위해 access time을 기록하지 않고, 보안성을 높이기 위해 setuid를 기본적으로 끄도록 한다. Error checksum을 보다 향상 된 fletcher4를 사용하고 (SHA256도 가능하나 CPU 부하가 큼), 이후 설정을 위해 기본적으로 mount되지 않 도록 한다. Mount point는 altroot 옵션을 사용하는 관계로 /로 설정하는 것으로 충분하다. 생성시에 mountpoint=/로 하게 되면 바로 root에 mount하려다 error를 내고 생성이 되지 않는 점을 주의해야한다. zpool create -o altroot=/mnt -o cachefile=/var/tmp/system.cache -O mountpoint=none -O atime=off -O setuid=off —O checksum=fletcher4 - O canmount=off system raidz1 /dev/gpt/disk0.eli /dev/gpt/disk1.eli /dev/gpt/disk2.eli /dev/gpt/disk3.eli /dev/gpt/disk4.eli /dev/gpt/ disk5.eli! zfs set mountpoint=/ system root에 실제로 mount할 partition을 생성한다. 기본적으로 특수 성격의 partition외에는 모든 파일들은 이 partition에 저장된다. 기본적으로 setuid를 활성화한다. root에 mount되는 특수한 partition이므로 mount하려 면 직접 mount 명령을 실행시켜야 한다. zfs create -o mountpoint=legacy -o setuid=on system/rootfs! zpool set bootfs=system/rootfs system! mount -t zfs system/rootfs /mnt 이 이후 필요에 따라 다른 옵션을 부여하는 partition들을 생성한다. 참고로 lz4 compression은 RELEASE-9.2 이후에 추가되었다. 기본적으로 system partition에서 모든 설정을 상속받기 때문에 변경하려는 option만 추가 해주면 되고, mountpoint가 부여되어 있기 때문에, 생성과 함께 mount된다. zfs create system/root! zfs create -o compress=lz4 system/tmp! zfs create -o canmount=off system/usr! zfs create -o setuid=on system/usr/local! zfs create -o compress=gzip system/usr/src! zfs create -o compress=lz4 system/usr/obj! zfs create -o compress=gzip system/usr/ports! zfs create -o compress=off system/usr/ports/distfiles! zfs create -o compress=off system/usr/ports/packages! zfs create -o canmount=off system/var! zfs create -o compress=gzip system/var/log! zfs create -o compress=lz4 system/var/audit! zfs create -o compress=lz4 system/var/tmp! zfs create -o compress=gzip system/var/mail! zfs create -o canmount=off system/home system/tmp와 system/var/tmp는 고유성질을 반영하기 위해 추가 설정이 필요하다. /home은 필요에 따라 / usr/home으로 link를 걸어둔다. chmod 1777 /mnt/tmp! chmod 1777 /mnt/var/tmp! zfs set exec=off system/tmp! zfs set exec=off system/var/tmp! cd /mnt/usr; ln -s /home . Swap 지원 또한 FreeBSD에서 구현하고 있다. zfs add system/swap! zfs set org.freebsd:swap=on system/swap! zfs set checksum=off system/swap 설치를 위해서는 shell을 끝내고 설치화면으로 되돌아가는 방법이 있으나, 여기서는 shell 명령어로 처리토록 한 다. LiveCD에서는 /usr/freebsd-dist directory에 모든 설치파일들이 압축파일 형태로 들어있다. 압축을 풀어 / mnt에 넣어주는 것만으로 설치가 가능하다. cd /usr/freebsd-dist! for D in base doc games kernel lib32 src; do! xz -d -c ${D}.txz | tar -C /mnt/ -xf -! done 실행중에 .profile 등이 생성되지 않는다는 error message를 보여주는데, 이는 /etc/profile 등을 /root/.profile 로 hard link를 생성하려고 하나 system/root로 /root가 별도의 partition을 가짐에 따라 hard link가 생성되지 않기에 생기는 error로 여기서는 일단 무시한다. 4. 부팅을 위한 추가 설정! Mount 작업을 위해 /mnt/etc/fstab에 zfs partition을 mount하는 설정을 추가한다. system/rootfs!/!zfs!rw,noatime!0!0 성능 향상을 위해 /mnt/etc/.conf에 kern.maxvnode 수를 크게 잡도록 한다. kern.maxvnodes=260000 부팅을 위해 /mnt/boot/loader.conf 파일을 생성한다. ZFS partition을 mount하기위한 설정, GELI partition을 attach하기 위한 설정, Firewall을 위한 설정 등이 있 다. 참고로 vfs.root.mountfrom 설정은 필요없다는 설명이 있었으나, 만약을 위해 설정해두는 것으로 한다. 보안을 위해 key file들은 복사하지 않고, 이후에 제작하는 USB disk에만 복사한다. # fs! zfs_load="YES"! vfs.root.mountfrom="zfs:system/rootfs"! # cryto! geom_eli_load="YES"! crypto_load="YES"! aesni_load="YES"! cryptodev_load="YES"! # ! pf_load="YES"! pflog_load="YES"! # tuning! vm.kem_size="32G"! # geli! geli_ada0p1_keyfile0_load="YES"! geli_ada0p1_keyfile0_type="ada0p1:geli_keyfile0"! geli_ada0p1_keyfile0_name="/boot/keys/ada0p1.key"! geli_ada1p1_keyfile0_load="YES"! geli_ada1p1_keyfile0_type="ada1p1:geli_keyfile0"! geli_ada1p1_keyfile0_name="/boot/keys/ada1p1.key"! geli_ada2p1_keyfile0_load="YES"! geli_ada2p1_keyfile0_type="ada2p1:geli_keyfile0"! geli_ada2p1_keyfile0_name="/boot/keys/ada2p1.key"! geli_ada3p1_keyfile0_load="YES"! geli_ada3p1_keyfile0_type="ada3p1:geli_keyfile0"! geli_ada3p1_keyfile0_name="/boot/keys/ada3p1.key"! geli_ada4p1_keyfile0_load="YES"! geli_ada4p1_keyfile0_type="ada4p1:geli_keyfile0"! geli_ada4p1_keyfile0_name="/boot/keys/ada4p1.key"! geli_ada5p1_keyfile0_load="YES"! geli_ada5p1_keyfile0_type="ada5p1:geli_keyfile0"! geli_ada5p1_keyfile0_name="/boot/keys/ada5p1.key" 시스템 설정을 위해 /mnt/etc/rc.conf 파일을 설정한다. ZFS 작동을 위한 설정, 잠자기 설정, hostname 설정, NIC 카드 설정, 기타 기본적으로 필요한 network service 설정 등이 이에 해당한다. # fs! zfs_enable="YES"! # system! powerd_enable="YES"! powerd_flags="-a adaptive -b adaptive"! hostname="skonnas.local"! # nic! ifconfig_bge0="DHCP"! # network! sshd_enable="YES"! ntpd_enable="YES"! ntpd_sync_on_start="YES"! sendmail_enable="NONE" 5. USB disk 제작! 부팅을 실제로 담당하는 USB disk 제작을 위해 우선 ZFS pool을 생성한다. 꼭 zfs로 할 필요는 없으나, 이후의 설정 관리를 위해 zfs로 하는 것으로 한다. 먼저 boot code를 넣어줄 boot partition과 ZFS partition을 생성한다. 그 이후 ZFS pool을 생성한다. gpart -s GPT da0! gpart add -b 34 -s 94 —t freebsd-boot da0! gpart add -t freebsd-zfs -l bootdisk da0! gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0! zpool -O mountpoint=none -O atime=off -O canmount=off -O checksum=fletcher4 boot /dev/gpt/bootdisk USB에서는 부팅에 필요한 데이터만 들어 있으면 되며, FreeBSD에는 모두 /boot에 담겨있다 (담겨있어야 한 다.) 이를 위해 /mnt/boot에 있는 전체 내용과, GELI attach를 위한 key file들과 system pool을 인식하기 위해 ZFS pool 정보를 담고 있는 system.cache 파일을 복사해야 한다. mkdir -p /var/tmp/bootdisk! mount -t zfs boot /var/tmp/bootdisk! cd /var/tmp/bootdisk; cp -Rp /mnt/boot .! cd /var/tmp/bootdisk/boot; cp -Rp /var/tmp/keys .! cd /var/tmp/bootdisk/boot/zfs; cp /var/tmp/system.cache . Reboot하여 정상적으로 작동하는지 확인하는 것이 마지막으로 할일. Reboot되기 전에 CD-ROM을 분리해서 LiveCD로 부팅하는 것을 방지해야 한다. 6. USB disk Backup! USB disk는 언제든지 파손되거나 분실될 위험을 가지고 있다. Key file은 USB disk에만 들어있는 관계로 한 번 잃어버리게 되면 그 여파가 상당히 크므로, backup이 필요하다. CD/DVD mastering 방식으로 읽어들여놓고 보관하는 것을 권장한다. 물론 이 파일이 유출되면 보안성이 낮아지므로 잘 관리해야한다. 7. Reference! 이 글은 전적으로 FreeBSD HOWTO (https://forums.freebsd.org/showthread.php?t=2775)에 기반하여 작성되었다. 원글은 2008년에 작성되었으나, 거의 수정없이 적용가능하다. 파티션 정책은 From sysinstall to ZFS-only configuration.: My work (http://blogs.freebsdish.org/pjd/ 2010/08/06/from-sysinstall-to-zfs-only-configuration/)에 기반하여 작성되었으며, Root on ZFS freebsd 9 (4k optimized) (http://www.aisecure.net/2012/01/16/rootzfs/)의 내용을 보충하였다. 기타 설정 파일들 /boot/loader.conf, /etc/rc.conf, /etc/sysctl.conf 등의 설정에 대해서는 Installation of a FreeBSD 9.2 system with ZFS-on-root over GELI (https://www.keltia.net/howtos/mfsbsd-zfs91/)의 내용을 참조하였다. 모두의 노력에 감사한다.