Компактная инсталляция FreeBSD 10 для центра сертификации

от автора

Однажды возникла необходимость в подчинённом, в рамках имеющейся инфраструктуры центре сертификации для ‘выездного’ использования — создания временных технологических сертификатов во время различных разъездов. Необходимо было сделать его мобильным и разумно, для данных целей, защищённым. Приемлемым был признан вариант с загрузочной флешкой с каким-нибудь *nix + openssl и типовым сценарием использования — имеющийся под рукой компьютер перезагружаем, вставляем флешку, загружаемся с неё, работаем, вытаскиваем флешку, перезагружаем компьютер. Секретные ключи данного ЦС, его файлы конфигурации, ключевые файлы для двухфакторной аутентификации могут быть вынесены на отдельные носители.

Начался выбора дистрибутива *nix. Требования были следующие:

  • дистрибутив должен быть поддерживаемым в актуальном состоянии, в том числе и OpenSSL
  • наличие i386 версии. Гигабайты памяти нам не понадобятся, а вот возможность запуска на нетбуке с x86 Intel Atom будет полезна
  • запуск системы из оперативной памяти, либо корректный запуск и работа с r/o разделов. В идеале, корректная работа с флешки при включенной защите от записи (при использовании Qumo серии Yin & Yan)
  • возможность компактной инсталляции
  • желателен достаточно большой срок поддержки релиза

В результате в финал вышли два дистрибутива — Alpine Linux и OpenBSD. Всё бы хорошо, и не было бы смысла писать эту статью, как ВНЕЗАПНО уточнились требования к операционной системе — потребовалась полная поддержка русского текста в Unicode в системной консоли. На ввод и на вывод.

Ну всё, приплыли. Оба финалиста выбыли из соревнования. Alpine Linux в силу используемой в нём компактной библиотеки C, а OpenBSD… ну, у неё другие сильные стороны. Однако это требование позволило иначе взглянуть на доступные дистрибутивы, и в фаворитах неожиданно оказалась FreeBSD. Консольный драйвер vt (ранее известный как newcons) поддерживает Unicode «из коробки», русские шрифты идут в комплекте, вышеописанные критерии в сумме исполняются достаточно полно. Бонусом можно реализовать возможность оффлайнового бинарного обновления системы на флешке.

Началось изучение возможности компактной инсталляции стандартными способами. Готовые наборы — tinybsd, nanobsd, picobsd произвели впечатление «старый, древний, ещё древнее». Одни только расчёты секторов, головок, цилиндров для разных типов флешек в picobsd… Прям времена MFM, RLL, ST-506…

Творения отдельных энтузиастов, различные miniBSD, easyBSD, mfsBSD выглядели поинтереснее, но в итоге всё равно было решено поиграть в доброго доктора Франкенштейна.

В данном примере мы сделаем загрузочную флешку с:

  • компактной инсталляцией FreeBSD 10.1
  • русифицированной системной консолью в Unicode
  • корневым разделом, монтирующимся в памяти
  • разделом для скриптов и файлов конфигурации нашего ЦС
  • шифрованным разделом для секретных ключей ЦС
  • FAT разделом для обмена с внешними системами запросами на сертификат и подписанными сертификатами
  • возможностью оффлайнового обновления системы
  • возможностью выбора ядра при загрузке (минимальное + GENERIC)
  • возможностью выбора образа системы при загрузке (эталонный + обновлённый до последней версии FreeBSD)

Подчеркну — пример демонстрационный, в него включено всё что можно. В реальной жизни вышеуказанные возможности применяются, естественно, выборочно. Нет особого смысла один раздел монтировать в памяти, а другой, с той же флешки — вживую. И да, это будет не слишком похоже на «ядро + systemd + шелл + openssl», но не ставилась же цель уместить всё на дискету. Времена не те. И с fdformat.com и 800.com не влезет… Поэтому помимо программ, без которых не обойтись, были оставлены программы, которые могут пригодиться — по работе с текстом, с дисками, с архивами. К тому же, для обеспечения заявленной возможности обновления системы, сильно её уродовать (свернуть всё в один crunchgen бинарник и засунуть в ядро) нельзя — а то freebsd-update не признает.

Забегая вперёд — размер системы в максимальной конфигурации (два ядра + два образа) у нас составит 48 Мб, а в минимальной (одно ядро + один образ) — 7⅓ Мб. Без установленных пакетов.

Возможность выбора ядра при загрузке оказалась очень удобна для отладки — если что-то не работает, то попеременно загружаясь с разными ядрами и одним и тем же образом системы можно определить в сборке чего проблема — ядра или мира.

Итак, начнём.

Нашу флешку будем делать на основе FreeBSD 10.1-RELEASE. Можно, конечно, -STABLE, но тогда не будет работать freebsd-update с бинарными обновлениями. В процессе работы мы скомпилируем мир и ядро, установим их в отдельный каталог, настроим систему, удалим ненужные файлы, после чего сделаем .iso и .img. Ничего сложного.

Создадим рабочее окружение. Можно в железе, но проще в виртуальной машине. Скачиваем FreeBSD-10.1-RELEASE-i386, устанавливаем, при выборе системных компонент включив установку исходных текстов. Дерево портов и игры не нужны. Загружаемся, заходим как root.

Подготовка

Всё будем делать из-под root.

Для удобства установим несколько переменных окружения, они будут использоваться далее по тексту. У пользователя root шелл по умолчанию csh, поэтому в /root/.login добавляем:

$ echo "setenv BASE /root/caBSD" >> /root/.login $ echo "setenv WORKDIR /root/caBSD/_work" >> /root/.login 

Да, нашу поделку назовём ЦСДПБ caBSD

Обновим систему до последнего patchlevel

$ freebsd-update fetch $ freebsd-update install  $ reboot 

Ядро, мир и исходники обновились. Перезагружаемся.

Создадим каталог /root/caBSD и рабочие подкаталоги в нём.

$ mkdir -p ${BASE}/{conf,tools,pkg.local} $ mkdir -p ${WORKDIR}/{vanilla,custom,mnt} 

/root/caBSD/_work/ — основной рабочий каталог, ${WORK}. Подкаталоги в нём:
vanilla/ — тут мы настраиваем корень будущей системы, installworld, installkernel
mnt/ — временная точка монтирования
custom/ — из содержимого этого каталога будем создавать .iso/.img образы

Компилируем мир и ядро

Создаём файл конфигурации для нашего ядра. На этапе отладки используем конфигурацию, базирующуюся на GENERIC и состоящую из нескольких строчек:

tools/CABSD-DEV

# # tools/CABSD-DEV  include		GENERIC  ident		CABSD-DEV  # Без модулей makeoptions	NO_MODULES=1  # Без драйвера консоли sc nodevice	sc nodevice	vga  # Так как модули не компилируем, то включаем необходимые в ядро options 	TMPFS		# Efficient memory filesystem options 	GEOM_ELI	# Disk encryption. device		crypto		# core crypto support  options 	NO_SWAPPING	# Disable swapping of stack pages 

Здесь мы берём GENERIC ядро, отключаем syscons (vt становится драйвером консоли по умолчанию), включаем три модуля — для тестирования загрузки системы вполне хватит.

Потом, когда всё заработает можно использовать минимальное ядро. Для него берём файл GENERIC ядра, удаляем всё лишнее (периодически компилируя и проверяя — грузится .iso или нет), оставляем необходимое и непонятное. Файл конфигурации нашего рабочего ядра стал выглядеть так:

tools/CABSD

# # tools/CABSD  cpu		I486_CPU cpu		I586_CPU cpu		I686_CPU  ident		CABSD  makeoptions	NO_MODULES=1		# Don't compile modules  options 	SCHED_ULE		# ULE scheduler options 	PREEMPTION		# Enable kernel thread preemption  options 	FFS			# Berkeley Fast Filesystem #options 	SOFTUPDATES		# Enable FFS soft updates support #options 	UFS_ACL			# Support for access control lists options 	MD_ROOT			# MD is a potential root device options		ROOTDEVNAME=\"ufs:/dev/md0\"	# The root device and filesystem type can be compiled in options 	MSDOSFS			# MSDOS Filesystem options 	CD9660			# ISO 9660 Filesystem options 	TMPFS			# Efficient memory filesystem #options 	NULLFS			# NULL filesystem #options 	PROCFS			# Process filesystem (requires PSEUDOFS) #options 	PSEUDOFS		# Pseudo-filesystem framework  options 	GEOM_PART_GPT		# GUID Partition Tables. options 	GEOM_LABEL		# Provides labelization options 	GEOM_ELI		# Disk encryption.  options 	COMPAT_FREEBSD4		# Compatible with FreeBSD4 options 	COMPAT_FREEBSD5		# Compatible with FreeBSD5 options 	COMPAT_FREEBSD6		# Compatible with FreeBSD6 options 	COMPAT_FREEBSD7		# Compatible with FreeBSD7 #options 	SCSI_DELAY=5000		# Delay (in ms) before probing SCSI #options 	SYSVSHM			# SYSV-style shared memory options 	SYSVMSG			# SYSV-style message queues options 	SYSVSEM			# SYSV-style semaphores options 	_KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions options 	KBD_INSTALL_CDEV	# install a CDEV entry in /dev options 	PROCDESC		# Support for process descriptors #options 	INCLUDE_CONFIG_FILE	# Include this file in kernel options 	NO_SWAPPING		# Disable swapping of stack pages  # To make an SMP kernel, the next two lines are needed options 	SMP			# Symmetric MultiProcessor Kernel device		apic			# I/O APIC  device		cpufreq			# CPU frequency control  # Bus support. device		acpi device		pci #device		eisa  # Floppy drives #device		fdc  # USB support https://www.freebsd.org/doc/en/books/handbook/usb-disks.html device		scbus			# SCSI bus (required for ATA/SCSI) device		da			# Direct Access (disks)  device		uhci			# UHCI PCI->USB interface device		ohci			# OHCI PCI->USB interface device		ehci			# EHCI PCI->USB interface (USB 2.0) device		xhci			# XHCI PCI->USB interface (USB 3.0) device		usb			# USB Bus (required) device		umass			# Disks/Mass storage - Requires scbus and da  device		cd			# CD  device		ukbd			# Keyboard  device		ahci			# AHCI-compatible SATA controllers device		ata			# Legacy ATA/SATA controllers options 	ATA_STATIC_ID		# Static device numbering #device		mvs			# Marvell 88SX50XX/88SX60XX/88SX70XX/SoC SATA #device		siis			# SiliconImage SiI3124/SiI3132/SiI3531 SATA  # atkbdc0 controls both the keyboard and the PS/2 mouse device		atkbdc			# AT keyboard controller device		atkbd			# AT keyboard  # Без этого не будет работать kbdcontrol (keymap, keyrate) device		kbdmux			# keyboard multiplexer  # vt is the new video console driver device		vt device		vt_vga  options		TERMINAL_NORM_ATTR=(FG_GREEN|BG_BLACK)  options 	MAXCONS=4		# number of virtual consoles  # Number of initial kernel page table pages used for early bootstrap. # This number should include enough pages to map the kernel, any # modules or other data loaded with the kernel by the loader, and data # structures allocated before the VM system is initialized such as the # vm_page_t array.  Each page table page maps 4MB (2MB with PAE). # Может понадобиться увеличить, но вроде работает и так #options		NKPT=31  device		crypto			# core crypto support  # Pseudo devices. device		loop			# Network loopback device		random			# Entropy device #device		padlock_rng		# VIA Padlock RNG device		rdrand_rng		# Intel Bull Mountain RNG #device		ether			# Ethernet support device		md			# Memory "disks"  # EOF 

Создаём файл настроек для компиляции мира. Читаем man src.conf, выписываем все WITHOUT_ в файл и вдумчиво изучаем. Приходит понимание, что понадобится не один, а целых два файла настроек — один для buildworld, второй плюсом для installworld.

tools/worldbuild.conf

# # tools/worldbuild.conf # WITHOUT_ACCT= WITHOUT_ACPI= WITHOUT_AMD= WITHOUT_APM= WITHOUT_ARM_EABI= WITHOUT_ASSERT_DEBUG= WITHOUT_AT= WITHOUT_ATM= WITHOUT_AUDIT= WITHOUT_AUTHPF= # Эту опцию включаем в tools/worldinstall.conf # На этапе компиляции она бесполезна # WITHOUT_BINUTILS= WITHOUT_BLUETOOTH= WITHOUT_BMAKE= WITHOUT_BSD_CPIO= WITHOUT_BSNMP= WITHOUT_CALENDAR= WITHOUT_CAPSICUM= WITHOUT_CDDL= WITHOUT_CLANG= WITHOUT_CLANG_EXTRAS= WITHOUT_CLANG_FULL= WITHOUT_CPP= # Не включаем, так как на libcrypt завязаны geli и openssl # WITHOUT_CRYPT= WITHOUT_CTM= WITHOUT_CXX= WITHOUT_DICT= WITHOUT_EXAMPLES= WITHOUT_FDT= WITHOUT_FLOPPY= WITHOUT_FMTREE= # Не вкючаем, иначе сломается загрузочное меню # WITHOUT_FORTH= # Не включаем, понадобится по условиям задачи # WITHOUT_FREEBSD_UPDATE= WITHOUT_GAMES= WITHOUT_GCC= WITHOUT_GCOV= WITHOUT_GDB= WITHOUT_GNU= WITHOUT_GNUCXX= WITHOUT_GNU_SUPPORT= WITHOUT_GPIB= WITHOUT_GPIO= WITHOUT_GROFF= WITHOUT_GSSAPI= WITHOUT_HTML= WITHOUT_HYPERV= WITHOUT_ICONV= WITHOUT_INET= WITHOUT_INET_SUPPORT= WITHOUT_INET6= WITHOUT_INET6_SUPPORT= WITHOUT_INFO= # Эту опцию включаем в tools/worldinstall.conf # WITHOUT_INSTALLLIB= WITHOUT_IPFILTER= WITHOUT_IPFW= WITHOUT_IPX= WITHOUT_IPX_SUPPORT= WITHOUT_JAIL= WITHOUT_KDUMP= WITHOUT_KERBEROS= WITHOUT_KERBEROS_SUPPORT= WITHOUT_KERNEL_SYMBOLS= WITHOUT_LDNS= WITHOUT_LDNS_UTILS= # Не включаем, системная консоль нужна # WITHOUT_LEGACY_CONSOLE= # А это имеет смысл только для amd64, а у нас i386 # WITHOUT_LIB32= WITHOUT_LIBCPLUSPLUS= # Не включаем, несколько локалей нам понадобятся. # Лишние потом удалим, оставим только нужные # WITHOUT_LOCALES= WITHOUT_LOCATE= WITHOUT_LPR= WITHOUT_LS_COLORS= WITHOUT_MAIL= WITHOUT_MAILWRAPPER= WITHOUT_MAKE= WITHOUT_MAN= WITHOUT_MAN_UTILS= # libncurces используется редакторами /usr/bin/vi и jupp из пакетов. # WITHOUT_NCURSESW= WITHOUT_NDIS= WITHOUT_NETCAT= WITHOUT_NETGRAPH= WITHOUT_NETGRAPH_SUPPORT= WITHOUT_NIS= WITHOUT_NLS= WITHOUT_NLS_CATALOGS= WITHOUT_NMTREE= WITHOUT_NS_CACHING= WITHOUT_NTP= WITHOUT_OPENSSH= # Если включить опцию, то openssl компилироваться не будет, # придётся его ставить из пакетов. Но в нашем случае # используем openssl из комплекта # WITHOUT_OPENSSL= WITHOUT_PAM_SUPPORT= WITHOUT_PC_SYSINSTALL= WITHOUT_PF= WITHOUT_PKGBOOTSTRAP= WITHOUT_PMC= WITHOUT_PORTSNAP= WITHOUT_PPP= WITHOUT_PROFILE= WITHOUT_QUOTAS= WITHOUT_RCMDS= WITHOUT_RCS= # Тема /rescue будет рассмотрена отдельно WITHOUT_RESCUE= WITHOUT_ROUTED= WITHOUT_SENDMAIL= WITHOUT_SHAREDOCS= WITHOUT_SOURCELESS= WITHOUT_SOURCELESS_UCODE= WITHOUT_SOURCELESS_HOST= WITHOUT_SSP= WITHOUT_SVNLITE= # syscons нам не нужен, мы будем использовать newcons - vt(4) WITHOUT_SYSCONS= WITHOUT_SYSINSTALL= # В качестве шелла используем tcsh # WITHOUT_TCSH= WITHOUT_TELNET= WITHOUT_TESTS= WITHOUT_TEXTPROC= # Эту опцию включаем в tools/worldinstall.conf # На этапе компиляции она бесполезна # WITHOUT_TOOLCHAIN= WITHOUT_UNBOUND= WITHOUT_USB= WITHOUT_UTMPX= # Не включаем, vt нам как раз и нужен # WITHOUT_VT= WITHOUT_WIRELESS= WITHOUT_WIRELESS_SUPPORT= WITHOUT_WPA_SUPPLICANT_EAPOL= WITHOUT_ZFS= # Не включаем, файлы tz нам понадобятся. # Лишние потом удалим, оставим только нужные # WITHOUT_ZONEINFO= 

tools/worldinstall.conf

# # tools/worldinstall.conf #  # Set to not install binutils (as, c++-filt, gconv, ld, nm, objcopy, objdump, readelf, size and strip) WITHOUT_BINUTILS=  # Set this if you do not want to install optional libraries. For example when creating a nanobsd(8) image. WITHOUT_INSTALLLIB=  # Set to not install programs used for program development, compilers, debuggers etc. The option does not work for build targets. # When set, it also enforces the following options: # WITHOUT_BINUTILS, WITHOUT_CLANG, WITHOUT_CLANG_EXTRAS, WITHOUT_CLANG_FULL, WITHOUT_GCC, WITHOUT_GDB WITHOUT_TOOLCHAIN= 

С такими опциями мы отключаем компиляцию как clang, так и gcc.

Приступаем к компиляции. На первом же шаге немного схитрим. При компиляции мира очень много времени уходит на компиляцию компилятора, но у нас же есть готовый, в базовой системе. К тому же только что обновлённый. Им мы и воспользуемся. Устанавливаем переменные

$ setenv CC `whereis -bq clang` $ setenv CPP `whereis -bq clang-cpp` $ setenv CXX `whereis -bq clang++` 

Создаём символьную ссылку на файл конфигурации ядра CABSD в соответствующий каталог (в данном примере пропустим отладочный этап с CABSD-DEV, сразу будем делать конечный вариант ядра).

$ ln -sf ${BASE}/tools/CABSD /usr/src/sys/i386/conf 

Компилируем. Дополнительные опции вида ‘-j N’ по желанию

$ cd /usr/src  # rm -rf /usr/obj  # Мир $ make buildworld USER=IT HOSTNAME=hq.example.net SRCCONF=${BASE}/tools/worldbuild.conf __MAKE_CONF=/dev/null  # Ядро $ make buildkernel USER=IT HOSTNAME=hq.example.net SRCCONF=${BASE}/tools/worldbuild.conf __MAKE_CONF=/dev/null KERNCONF=CABSD  $ cd ${BASE} 

Параметры USER и HOSTNAME декоративные — можно указать что-то своё, чтобы в новой системе при загрузке, при uname -a, да и в других местах выводилось гордое «IT@hq.example.net» а не «root@localhost».

Теперь устанавливаем систему в рабочий каталог. Обратите внимание — между этапами компиляции и установки не стоит посредством freebsd-update обновлять систему или выключать параметры в файле worldbuild.conf.

$ cd /usr/src  # Устанавливаем мир. Второй файл с параметрами мы указываем через параметр __MAKE_CONF - фактически он приплюсуется к первому. $ make installworld DESTDIR=${WORKDIR}/vanilla SRCCONF=${BASE}/tools/worldbuild.conf __MAKE_CONF=${BASE}/tools/worldinstall.conf  # Устанавливаем etc $ make distribution DESTDIR=${WORKDIR}/vanilla SRCCONF=${BASE}/tools/worldbuild.conf __MAKE_CONF=${BASE}/tools/worldinstall.conf  # Устанавливаем наше минимальное ядро $ make installkernel DESTDIR=${WORKDIR}/vanilla SRCCONF=${BASE}/tools/worldbuild.conf __MAKE_CONF=${BASE}/tools/worldinstall.conf KERNCONF=CABSD  # Копируем второе ядро, GENERIC - берём его из /boot/kernel базовой системы # Устанавливаем во временный каталог $ mkdir -p ${WORKDIR}/vanilla/tmp/boot/kernel # И почти 800 модулей в придачу. Потом удалим лишние. $ cp -p /boot/kernel/{kernel,*.ko} ${WORKDIR}/vanilla/tmp/boot/kernel/  $ chflags -R noschg ${WORKDIR}/vanilla  $ cd ${BASE} 

Если не включать опцию WITHOUT_RESCUE= при компиляции мира, то мы можем заменить файлы в bin, sbin, usr/bin, usr/sbin на их аналоги из rescue, хотя целесообразность этого не очевидна. Вся система у нас находится на одном разделе, при загрузке монтирующемся в памяти — поэтому варианты с несмонтировавшемся usr практически исключены, и смысл замены может быть лишь в возможности уменьшить размер системы за счёт удаления библиотек, ставших ненужными из-за того, что использующие их программы заменены на слинкованный статически rescue. Впрочем, экономию надо просчитывать, а в нашем примере мы rescue компилировать не будем.

Если будете заменять, то можно использовать скрипт (подсмотрен в mfsBSD)

tools/_linkrescue.sh

#!/bin/csh  # # tools/_linkrescue.sh  cd ${WORKDIR}/vanilla  echo "Before:"  du -hc bin sbin usr/bin usr/sbin rescue  foreach FILE (`ls rescue`) 	if ( -f bin/${FILE} ) then 		ln -f rescue/${FILE} bin/${FILE} 	else if ( -f sbin/${FILE} ) then 		ln -f rescue/${FILE} sbin/${FILE} 	else if ( -f usr/bin/${FILE} ) then 		ln -f rescue/${FILE} usr/bin/${FILE} 	else if ( -f usr/sbin/${FILE} ) then 		ln -f rescue/${FILE} usr/sbin/${FILE} 	endif end  echo "After:"  du -hc bin sbin usr/bin usr/sbin rescue  cd ${BASE} 

Устанавливаем пакеты

Для удобства операторов нашего ЦС мы установим файловый менеджер. Популярный mc занимает 158 Мб, mc-light — 6 Мб, deco — 217 Mb со всеми зависимостями. Если ставить из портов, то, вероятно, можно было бы немного уменьшить размер с помощью опций компиляции, но в пакетах нашелся компактный консольный файловый менеджер clex — 0.3 Мб, который мы и установим.

Второй момент — редактор с поддержкой Unicode. Формально, по букве задания, поддержка была обеспечена — русский текст ввести в консоли в командной строке csh можно (echo привет), вывести на экран тоже (cat /usr/bin/help), но хотелось обеспечить более комфортную работу с текстами — отредактировать какой-нибудь файл, вести записи, мало ли. Комплектный /usr/bin/ee русский текст в Unicode файле показывает как "-PM-7M-PM-?M", /usr/bin/vi всё показывает корректно (правда он требует библиотеку libncursesw, поэтому придётся выключить опцию WITHOUT_NCURSESW= в tools/worldbuild.conf), /rescue/vi — "\xd0\xb7\xd0\xb0".

В пакетах нашлись несколько небольших редакторов с поддержкой Unicode, в частности: puff, отображающий текст как «запи~Аи в об~@аз» (в оригинале «записи в образ»); le — всем хорош, но требует библиотеки C++; и jupp, который всё показывает корректно, и, как и vi требует libncurses. Причём jupp представляет собой переработанную версию редактора joe, но если оригинальный joe из пакетов тянет за собой 49 Мб зависимостей, то jupp размером с vi.

Причём все проблемы у редакторов ee и puff лишь с отображением русских букв, при вводе «вслепую» всё сохраняется корректно.

В итоге проблема выбора между комплектным vi и jupp из пакетов была переложена на плечи потенциальных пользователей, которые проголосовали за jupp — понравилась подсветка синтаксиса и сравнительная дружелюбность редактора.

Устанавливаем misc/clex и editors/jupp. Какие-то пользовательские пакеты можно положить в каталог ${BASE}/pkg.local/, при установке они добавятся.

# Пакеты устанавливаем в два захода - сначала скачиваем их, добавляем # свои, если есть, а затем устанавливаем в chroot окружении.  # Устанавливаем pkg в базовую систему $ pkg -N || env ASSUME_ALWAYS_YES=YES pkg bootstrap  # БД пакетов держим в каталоге внутри ${WORKDIR}, чтобы не пересекалась с БД из базовой системы $ mkdir -p ${WORKDIR}/pkg.db $ setenv PKG_DBDIR ${WORKDIR}/pkg.db  # Обновим БД $ pkg-static update  # Пакеты пока будут внутри ${WORKDIR}/vanilla, устанавливать их будем в chroot $ mkdir -p ${WORKDIR}/vanilla/tmp/pkg.files/All  # Скачиваем пакеты, зависимости разрешаются автоматически. $ pkg-static fetch --output ${WORKDIR}/vanilla/tmp/pkg.files --dependencies --yes misc/clex editors/jupp $ unsetenv PKG_DBDIR  # Добавляем пакеты из локального каталога (${BASE}/pkg.local). # Удовлетворение зависимостей - на совести пользователя $ install -m 0644 ${BASE}/pkg.local/* ${WORKDIR}/vanilla/tmp/pkg.files/All  # Если это не первый запуск, то удалим БД с информацией об установленных пакетах $ rm -f ${WORKDIR}/vanilla/var/db/pkg/local.sqlite  # Устанавливаем пакеты $ cd ${WORKDIR}/vanilla $ find tmp/pkg.files/All/* -type f -exec pkg-static -c ${WORKDIR}/vanilla add {} \;  $ cd ${BASE} 

Настраиваем систему

В chroot окружении создаём пользователя ‘ca’. Для возможности монтирования устройств добавляем в группу operator. В качестве шелла используем csh, опция WITHOUT_TCSH в tools/worldbuild.conf не включена.

$ chroot ${WORKDIR}/vanilla pw useradd ca -c "Certification authority operator" -m -G operator -s /bin/csh  # Получаем и сохраняем в файл числовые UID и GID (по идее 1001:1001, но мало ли...) $ chroot ${WORKDIR}/vanilla id -u ca > ${WORKDIR}/ca.uid $ chroot ${WORKDIR}/vanilla id -g ca > ${WORKDIR}/ca.gid  # Включаем пользователю UTF-8 в системной консоли $ echo "setenv MM_CHARSET UTF-8" >> ${WORKDIR}/vanilla/home/ca/.login $ echo "setenv LANG en_US.UTF-8" >> ${WORKDIR}/vanilla/home/ca/.login  # Добавляем в конец path каталог со скриптами ЦС (раздел с ними будем монтировать в каталог /ca) # Необходимо убедиться, что в системе установлен лишь один openssl - либо из пакетов, либо системный. $ echo "set path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin /ca)" >> ${WORKDIR}/vanilla/home/ca/.cshrc  # vi мы не используем, устанавливаем свою переменную EDITOR $ echo "setenv EDITOR jupp" >> ${WORKDIR}/vanilla/home/ca/.cshrc  # Можно добавить переменные, используемые в конфигурации ЦС $ echo "setenv OPENSSL_CONF /ca/conf/ca.conf" >> ${WORKDIR}/vanilla/home/ca/.login $ echo "setenv CA_OID 1.3.6.1.4.1.nnnnn" >> ${WORKDIR}/vanilla/home/ca/.login 

Ещё у нас есть скрипт conf/ca.sh, который мы запустим в chroot окружении из-под пользователя ‘ca’ для выполнения некоторых действий в домашнем каталоге пользователя с его правами.

conf/ca.sh

#!/bin/sh -ex ## # conf/ca.sh ##  cd ~  # Здесь мы из-под пользователя 'ca' делаем какие-нибудь вещи  # 1. Создадим файл конфигурации для файлового менеджера clex  mkdir -p ~/.config/clex  cat << EOF > ~/.config/clex/config # # CLEX configuration file # CMD_F3=more -- $f CMD_F4=puff -- $f CMD_F5=cp -ir -- $f $2 CMD_F6=mv -i -- $f $2 CMD_F7=mkdir --  CMD_F8=rm -- $f EOF  # 2. Создадим файл конфигурации для vi  #cat << EOF > ~/.nexrc #set verbose showmode #set nomesg #EOF  # 3. Создадим файл .logout, он потребуется позже touch .logout 

Данный скрипт необходимо запустить до чистки системы, пока в ${WORKDIR}/vanilla ещё есть su.

# Устанавливаем conf/ca.sh с правами r-xr-xr-x в ${WORKDIR}/vanilla/home/ca $ install -m 0555 -o root -g wheel conf/ca.sh ${WORKDIR}/vanilla/home/ca/  # Запускаем $ chroot ${WORKDIR}/vanilla su - ca -c /home/ca/ca.sh  # Удаляем $ rm -f ${WORKDIR}/vanilla/home/ca/ca.sh 

Создаём файлы конфигурации. Все файлы лежат в каталоге ${BASE}/conf, откуда мы их потом установим в нужные места.

conf/fstab

## # conf/fstab ## /dev/md0		/	ufs	rw					0 0  # 50 Мб на /tmp. tmpfs			/tmp	tmpfs	rw,mode=1777,size=50m,noexec 		0 0  # noauto /dev/gpt/ca		/ca		ufs	rw,noauto,noatime		0 0 /dev/gpt/keys.eli	/ca.keys	ufs	ro,noauto,noatime		0 0 /dev/gpt/exchange	/ca.certs	msdosfs	rw,longnames,-u=ca,-g=ca,-m=0640,-M=0750,noauto,noatime	0 0 

Корневой раздел у нас монтируется из mfs, а /tmp в tmpfs. Можно /tmp отдельно не монтировать, оставить в корневом разделе, но тогда при создании флешки необходимо выделить больше свободного места для него внутри образа системы. При обновлении системы /tmp используется freebsd-update для извлечения файлов, самый большой из них в нашей конфигурации это GENERIC ядро, около 17 Мб, поэтому сильно маленьким этот раздел лучше не делать.

Раздел /dev/gpt/ca предназначен для скриптов и файлов конфигурации нашего ЦС, /dev/gpt/keys.eli — шифрованный с помощью geli раздел /dev/gpt/keys с секретными ключами, /dev/gpt/exchange — FAT раздел для обмена с внешними системами. Его мы монтируем с поддержкой длинных имён, но без поддержки их перекодировки — принято принципиальное решение об использовании лишь ASCII символов в именах файлов на этом разделе. Поэтому и систему, и ядро компилируем без поддержки iconv.

Эти разделы мы автоматически при старте не монтируем (параметр noauto), так как при каких-то неполадках (трижды неправильно введённый пароль для geli, например, что не позволит создать раздел keys.eli) нас выкинет в однопользовательский режим. У оператора будет шок и моральная травма. Поэтому эти разделы будем монтировать в userspace пользователя ‘ca’, для чего при создании включим его в группу operator и настройками devfs и sysctl (ниже) предоставим необходимые полномочия.

Обратите внимание — в fstab раздел с ключами для ЦС монтируется в режиме только для чтения, поэтому перед созданием ключей надо не забыть перемонтировать его для записи (mount -u -o rw /dev/gpt/keys.eli).

# Добавляем автоматическое монтирование разделов ЦС. Применимо только для флешки, # при загрузке с CD будут выскакивать ошибки об отсутствующих разделах $ echo "mount /dev/gpt/ca" >> ${WORKDIR}/vanilla/home/ca/.login $ echo "mount /dev/gpt/keys.eli" >> ${WORKDIR}/vanilla/home/ca/.login $ echo "mount /dev/gpt/exchange" >> ${WORKDIR}/vanilla/home/ca/.login  # И размонтирование $ echo "umount /dev/gpt/ca" >> ${WORKDIR}/vanilla/home/ca/.logout $ echo "umount /dev/gpt/keys.eli" >> ${WORKDIR}/vanilla/home/ca/.logout $ echo "umount /dev/gpt/exchange" >> ${WORKDIR}/vanilla/home/ca/.logout 

Обратите внимание — если файлы .login и .cshrc уже существовали в каталоге пользователя ‘ca’, и мы могли дописывать туда без опасения порчи владельца или прав, то файл .logout при заведении пользователя не создавался. Поэтому, если мы не хотим, чтобы его владельцем оказался root:ca, необходимо создать его из-под пользователя ca, хотя бы с помощью скрипта conf/ca.sh

Теперь мы предоставим возможность нашему пользователю ‘ca’ возможность монтировать сторонние флешки и разделы на нашей флешке (с настройками ЦС, ключами, разделом обмена).

conf/devfs.rules

## # conf/devfs.rules ## [localrules=5] # Правило для нашей мультифлешки # '/dev/' не указываем add path 'gpt/*' group operator mode 0660 # Правило для обычных флешек add path 'da*' group operator mode 0660 

Флешку будем разбивать с использованием GPT, её разделы адресовать по меткам вида /dev/gpt/label

conf/sysctl.conf

## # conf/sysctl.conf ##  # allow regular users to mount file systems vfs.usermount=1 

Устанавливаем переменную в sysctl.conf

conf/ttys

## # conf/ttys ##  # # $FreeBSD: releng/10.1/etc/etc.i386/ttys 267236 2014-06-08 17:50:07Z nwhitehorn $ #	@(#)ttys	5.1 (Berkeley) 4/17/89 # console	none				unknown	off secure  # Терминал с автологином 'ca' ttyv0	"/usr/libexec/getty CA"		xterm	on  secure  # Терминал для 'root' ttyv1	"/usr/libexec/getty Pc"		xterm	on  secure 

Для системной консоли при старте запускаем два терминала (оба должны быть xterm). Первый — с автологином пользователя ‘ca’ (см. описание gettytab ниже), второй для ‘root’, обычный вход с запросом пароля.

conf/gettytab

## # conf/gettytab ##  # $FreeBSD: releng/10.1/etc/gettytab 241708 2012-10-18 22:20:02Z peterj $ #	from: @(#)gettytab	5.14 (Berkeley) 3/27/91 # default:\ 	:cb:ce:ck:lc:fd#1000:im=\r\n%s/%m (%h) (%t)\r\n\r\n:sp#1200:\ 	:if=/etc/issue:  P|Pc|Pc console:\ 	:ht:np:sp#9600:  # Прописываем автологин для пользователя 'ca' CA:\ 	:al=ca:tc=Pc: 

В настройках оставляем лишь несколько секций — default, Pc — для пользователя ‘root’ и создаём новую секцию CA, в которой прописываем автологин пользователя ‘ca’, а остальное наследуется из Pc и default.

Далее создаём файл issue, в котором сделаем напоминание о необходимости проверить установленное время на том компьютере, с которого загрузились. Интернет не предусмотрен by design, поэтому часики будем при необходимости устанавливать вручную. Благо, для данного ЦС точность плюс-минус минута абсолютно не критична.

conf/issue

====^^^=^^=^^=^^=^^== == Проверьте время == 

Берём файл etc/defaults/rc.conf, копируем в наш conf/ и начинаем отключать всё лишнее.

conf/rc.conf

## # conf/rc.conf ##  # # $FreeBSD: releng/10.1/etc/defaults/rc.conf 273188 2014-10-16 22:00:24Z hrs $  ############################################################## ###  Important initial Boot-time options  #################### ##############################################################  rc_debug="NO"		# Set to YES to enable debugging output from rc.d rc_info="YES"		# Enables display of informational messages at boot. devd_enable="NO" 	# Run devd, to trigger programs on device tree changes. devd_flags=""		# Additional flags for devd(8).  gptboot_enable="YES"	# GPT boot success/failure reporting.  # GELI disk encryption configuration. geli_devices="gpt/keys"	# List of devices to automatically attach in addition to 			# GELI devices listed in /etc/fstab. geli_tries=""		# Number of times to attempt attaching geli device. 			# If empty, kern.geom.eli.tries will be used. geli_default_flags=""	# Default flags for geli(8). geli_autodetach="YES"	# Automatically detach on last close. 			# Providers are marked as such when all file systems are 			# mounted. # Example use. #geli_devices="da1 mirror/home" #geli_da1_flags="-p -k /etc/geli/da1.keys" #geli_da1_autodetach="NO" #geli_mirror_home_flags="-k /etc/geli/home.keys"  fsck_y_enable="NO"	# Set to YES to do fsck -y if the initial preen fails. fsck_y_flags=""		# Additional flags for fsck -y # Разделы на флешке создали без поддержки снапшотов, поэтому параметр устанавливаем в "NO" background_fsck="NO"	# Attempt to run fsck in the background where possible.  ############################################################## ###  Network configuration sub-section  ###################### ##############################################################  ### Basic network and firewall/security options: ### hostname="root2sub1"		# Set this! hostid_enable="NO"		# Set host UUID.  #network_interfaces="lo0"	# List of network interfaces (or "auto"). #ifconfig_lo0="inet 127.0.0.1"	# default loopback device configuration. network_interfaces=""		# List of network interfaces (or "auto").  ### Network daemon (miscellaneous) ### hostapd_enable="NO"		# Run hostap daemon. syslogd_enable="NO"		# Run syslog daemon (or NO).  ### IPv6 options: ### ip6addrctl_enable="NO"		# Set to YES to enable default address selection ipv6_network_interfaces="none"	# List of IPv6 network interfaces 				# (or "auto" or "none").  ############################################################## ###  Mail Transfer Agent (MTA) options  ###################### ##############################################################  # Settings for /etc/rc.sendmail and /etc/rc.d/sendmail: sendmail_enable="NO"	# Run the sendmail inbound daemon (YES/NO).  ############################################################## ###  Miscellaneous administrative options  ################### ##############################################################  # Это для того, чтоб юзер 'ca' мог монтировать флешки devfs_rulesets="/etc/defaults/devfs.rules /etc/devfs.rules" # Files containing 							    # devfs(8) rules. devfs_system_ruleset="localrules"	# The name (NOT number) of a ruleset to apply to /dev devfs_set_rulesets=""	# A list of /mount/dev=ruleset_name settings to 			# apply (must be mounted already, i.e. fstab(5)) devfs_load_rulesets="YES"	# Enable to always load the default rulesets  cron_enable="NO"	# Run the periodic job daemon. crashinfo_enable="NO"	# Automatically generate crash dump summary.  kern_securelevel_enable="NO"	# kernel security level (see security(7)) kern_securelevel="-1"	# range: -1..3 ; `-1' is the most insecure 			# Note that setting securelevel to 0 will result 			# in the system booting with securelevel set to 1, as 			# init(8) will raise the level when rc(8) completes.  update_motd="NO"	# update version info in /etc/motd (or NO) entropy_file="NO"	# Set to NO to disable caching entropy through reboots. 			# /var/db/entropy-file is preferred if / is not avail. dmesg_enable="YES"	# Save dmesg(8) to /var/run/dmesg.boot  newsyslog_enable="NO"	# Run newsyslog at startup. mixer_enable="NO"	# Run the sound mixer.   ############################################################## ###  System console options  ################################# ##############################################################  #keyboard=""		# keyboard device to use (default /dev/kbd0). keymap="ru.win"		# keymap in /usr/share/{syscons,vt}/keymaps/* (or NO). keyrate="fast"		# keyboard rate to: slow, normal, fast (or NO). #keybell="NO" 		# See kbdcontrol(1) for options.  Use "off" to disable. #keychange="NO"		# function keys default values (or NO). #cursor="NO"		# cursor type {normal|blink|destructive} (or NO). #scrnmap="NO"		# screen map in /usr/share/syscons/scrnmaps/* (or NO). #font8x14="NO"		# font 8x14 from /usr/share/{syscons,vt}/fonts/* (or NO). #font8x8="NO"		# font 8x8 from /usr/share/{syscons,vt}/fonts/* (or NO). blanktime="NO"		# blank time (in seconds) or "NO" to turn it off. moused_nondefault_enable="NO" # Treat non-default mice as enabled unless 			       # specifically overriden in rc.conf(5).  # Сюда, при наличии своих шрифтов будет дописана строка font8x16="xxx.fnt" 

Секция с параметрами geli_ понадобится позже. Прописываем hostname. Указываем параметры для devfs — чтобы подтягивались наши локальные правила для монтирования флешек. Стоит иметь в виду, что freebsd-update не работает при установке kern.securelevel > 0. И, напоследок, в секции System console options настроим переключение на русский и свойства клавиатуры. Файл русского шрифта здесь мы не указываем — если есть свой, то добавим позже, если своего нету — то пусть используется системный

conf/hosts

## # conf/hosts ## 127.0.0.1	localhost 

Пусть будет.

conf/termcap

# # conf/termcap #  #	@(#)termcap.src	8.2 (Berkeley) 11/17/93 # $FreeBSD: releng/10.1/share/termcap/termcap.src 267734 2014-06-22 16:48:21Z gavin $  xterm|X11 terminal emulator:\ 	:tc=xterm-new: # To add a termcap entry under FreeBSD for a new terminal type, insert # the entry in the appropriate location in /etc/termcap then issue this # command: # #  cap_mkdb /etc/termcap # xterm-clear:\ 	:te=\E[?1049l:ti=\E[?1049h:\ 	:tc=xterm-new: xterm-new|modern xterm:\ 	:@7=\EOF:@8=\EOM:F1=\E[23~:F2=\E[24~:K2=\EOE:Km=\E[M:\ 	:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\E[15~:k6=\E[17~:\ 	:k7=\E[18~:k8=\E[19~:k9=\E[20~:k;=\E[21~:kI=\E[2~:\ 	:kN=\E[6~:kP=\E[5~:kd=\EOB:kh=\EOH:kl=\EOD:kr=\EOC:ku=\EOA:\ 	:tc=xterm-basic: # # This chunk is used for building the VT220/Sun/PC keyboard variants. xterm-basic|modern xterm common:\ 	:am:bs:km:mi:ms:ut:xn:AX:\ 	:Co#8:co#80:kn#12:li#24:pa#64:\ 	:AB=\E[4%dm:AF=\E[3%dm:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:\ 	:DO=\E[%dB:LE=\E[%dD:RI=\E[%dC:UP=\E[%dA:ae=\E(B:al=\E[L:\ 	:as=\E(0:bl=^G:cd=\E[J:ce=\E[K:cl=\E[H\E[2J:\ 	:cm=\E[%i%d;%dH:cs=\E[%i%d;%dr:ct=\E[3g:dc=\E[P:dl=\E[M:\ 	:ei=\E[4l:ho=\E[H:im=\E[4h:is=\E[!p\E[?3;4l\E[4l\E>:\ 	:kD=\E[3~:kb=^H:ke=\E[?1l\E>:ks=\E[?1h\E=:kB=\E[Z:le=^H:md=\E[1m:\ 	:me=\E[m:ml=\El:mr=\E[7m:mu=\Em:nd=\E[C:op=\E[39;49m:\ 	:rc=\E8:rs=\E[!p\E[?3;4l\E[4l\E>:sc=\E7:se=\E[27m:sf=^J:\ 	:so=\E[7m:sr=\EM:st=\EH:\ 	:ue=\E[24m:up=\E[A:us=\E[4m:ve=\E[?12l\E[?25h:vi=\E[?25l:vs=\E[?12;25h:  # # END OF TERMCAP # ------------------------ 

Сделаем урезанную версию файла termcap, оставим там только xterm* терминалы — это позволит значительно уменьшить размер termcap.db.

conf/freebsd-update.conf

## # conf/freebsd-update.conf ## # $FreeBSD: releng/10.1/etc/freebsd-update.conf 258121 2013-11-14 09:14:33Z glebius $  # Trusted keyprint. KeyPrint 800651ef4b4c71c27e60786d7b487188970f4b4169cc055784e21eb71d410cc5  # Server or server pool from which to fetch updates. ServerName update.FreeBSD.org  # Components of the base system which should be kept updated. Components world kernel  # Directory in which to store downloaded updates and temporary # files used by FreeBSD Update. WorkDir /mnt  # When installing a new kernel perform a backup of the old one first # so it is possible to boot the old kernel in case of problems. BackupKernel no 

В нашем случае флешку с обновлениями мы будем монтировать в /mnt. При обновлении ядра старое не сохраняем.

Движимые человеколюбием к потенциальным операторам системы, которые вовсе не профессиональные пользователи FreeBSD, создадим ещё несколько файлов:

conf/motd

Для вызова справки введите 'help'  Переключение раскладки клавиатуры - Ctrl+Shift 

И два файла — help и manpath, которые мы потом разместим в usr/bin

conf/help

#!/bin/sh ## # conf/help ##  cat << EOF Для записи изменений в образ mfs2: $ mount /dev/gpt/system /mnt $ mdconfig -a -t vnode -f /mnt/mfs2 -u 1 $ mount /dev/md1 /media ... Записываем в /mеdia ... $ umount /media $ mdconfig -d -u 1 $ umount /mnt  Для монтирования MS DOS (as root) $ mount_msdosfs -u ca -g ca -m 0640 -M 0750 -l /dev/da1s1 /mnt (as user) $ mount_msdosfs -m 0640 -M 0750 -l /dev/da1s1 ~/mnt  Для конвертации DOS строк (CR/LF) в Unix формат: $ tr -d '\r' < in.txt > out.txt  Для установки времени (as root) $ date ГГММДДччмм.сс и проверить временную зону  Создать memory disk $ mount -t tmpfs -o size=100m tmpfs ~/mnt EOF 

Так как man страниц в комплекте не будет, то сделаем небольшой help

conf/manpath

#!/bin/sh ## # conf/manpath ##  # Так как систему компилировали с WITHOUT_MAN=, то утилита manpath отсутствует, а без неё не работает whereis  echo "" 

Второй файл, manpath — это заглушка для обхода неправильного поведения утилиты whereis, которая, даже при указании поиска только по двоичным файлам (‘-b’) завершается с ошибкой при безуспешной попытке вызова утилиты manpath, которой нет в нашей системе в силу компиляции мира с опцией WITHOUT_MAN=.

Итак, устанавливаем наши файлы из conf/ в ${WORKDIR}/vanilla и продолжаем настройку

# в etc # Разбито на две строки для лучшей читаемости $ install -m 0644 -o root -g wheel conf/{devfs.rules,freebsd-update.conf,fstab,gettytab} ${WORKDIR}/vanilla/etc/ $ install -m 0644 -o root -g wheel conf/{hosts,issue,motd,rc.conf,sysctl.conf,ttys} ${WORKDIR}/vanilla/etc/  # в usr/bin $ install -m 0555 -o root -g wheel conf/{help,manpath} ${WORKDIR}/vanilla/usr/bin/  # в usr/share/misc $ install -m 0644 -o root -g wheel conf/termcap ${WORKDIR}/vanilla/usr/share/misc/  # Устанавливаем временную зону. $ tzsetup -s -C ${WORKDIR}/vanilla Asia/Yekaterinburg  # Так как загружаться с флешки мы будем на компьютерах, у которых основная # система Windows, то часы на них установлены по местному времени, а не UTC. # Empty file. Its presence indicates that the machine's CMOS clock is set to local time. $ touch ${WORKDIR}/vanilla/etc/wall_cmos_clock  # Устанавливаем пароль для root (по желанию) $ pw -V ${WORKDIR}/vanilla/etc usermod root -h 0 

Если хочется видеть в консоли другой шрифт, например старый добрый keyrus, то по ссылке с habrahabr.ru/post/137544 можно взять keyrus.bdf и добавить его в нашу систему.

$ vtfontcvt tools/keyrus.bdf keyrus.fnt $ install -m 0444 -o root -g wheel keyrus.fnt ${WORKDIR}/vanilla/usr/share/vt/fonts/ $ echo 'font8x16="keyrus.fnt"' >> ${WORKDIR}/vanilla/etc/rc.conf 

Настройка загрузки

Теперь настал черёд ответственного этапа по настройки загрузки системы. Ядра, модули, загрузчик, загрузочное меню. Последовательность загрузки описана в соответствующих разделах handbook, поэтому здесь подробно её освещать нет смысла. Важно то, что загрузчику — loader’у мы должны предъявить ядро, а ядру уже файл с образом нашей системы. Система в ${WORKDIR}/vanilla почти готова, поэтому переносим оттуда ядра и загрузочные файлы в каталог ${WORKDIR}/custom.

# Создаём каталоги в custom/boot/ для файла настроек loader_default.conf и два каталога для ядер # boot/kernel/ - каталог для GENERIC ядра, а boot/kernel.cabsd/ - для нашего самодельного $ mkdir -p ${WORKDIR}/custom/boot/{defaults,kernel,kernel.cabsd}  # Переносим ядра  # Основное (мини) ядро $ cp -p ${WORKDIR}/vanilla/boot/kernel/kernel ${WORKDIR}/custom/boot/kernel.cabsd/  # Запасное (GENERIC) ядро $ cp -p ${WORKDIR}/vanilla/tmp/boot/kernel/kernel ${WORKDIR}/custom/boot/kernel/ 

Нам придётся нарисовать загрузочное меню. У нас два ядра и будет два образа системы. В меню мы включим три варианта: mff, mmf, ffmm

  1. ядро caBSD с первым образом системы
  2. ядро GENERIC с модулями и первым образом системы
  3. ядро GENERIC с модулями и вторым образом системы

Первый вариант и будет конфигурацией по умолчанию, и предполагаем, что обычно в ней и будет работать пользователь.

У нас будет три файла конфигурации для загрузчика — loader.conf для первой конфигурации, loader_gen1.conf и loader_gen2.conf — для GENERIC ядра с первым и вторым образом соответственно, отличающиеся лишь именем файла образа. Итак,

conf/loader.conf

## # conf/loader.conf ##  # Задержка перед автозагрузкой (сек) autoboot_delay="5"  # Эти два параметра обязательные mfs_load="YES" mfs_type="md_image"  # Указываем загрузку с образа mfs1 (мы его сожмём gzip, но расширение .gz не указываем) mfs_name="/mfs1"  # Полный путь к файлу ядра будет /boot/${kernel}/${bootfile} kernel="kernel.cabsd"	# /boot sub-directory containing kernel and modules #bootfile="kernel"	# Kernel name (possibly absolute path) #kernel_options=""	# Flags to be passed to the kernel  # Указываем опции для нашего newcons: # https://www.freebsd.org/cgi/man.cgi?query=vt(4)  # в ядро caBSD мы не включали sc, там есть только vt, поэтому параметр можно не указывать. # Но для GENERIC ядер он понадобится, а то UTF-8 не будет. # Поэтому включим здесь kern.vty=vt  # Enable halt keyboard combination. kern.vt.kbd_halt=1 # Enable power off key combination. kern.vt.kbd_poweroff=1 # Enable reboot key combination, usually Ctrl+Alt+Del. kern.vt.kbd_reboot=1 # Enable debug request key combination, usually Ctrl+Alt+Esc. kern.vt.kbd_debug=0 # Enable panic key combination. kern.vt.kbd_panic=0 

В loader.conf мы указываем параметры для первого варианта загрузки — ядро caBSD и образ mfs1 (его мы создадим чуть позже). Необходимо иметь в виду, что при нашей конфигурации меню, параметры, указанные в этом файле, будут наследоваться файлами настроек для второго и третьего варианта. То есть, если в loader.conf будет прописано что-то странное, вроде kernel_options="-s" — загрузка в однопользовательском режиме, то в нём будут загружаться все три варианта. Поэтому сомнительные переменные, прописанные в loader.conf мы будем перезаписывать (обнулять) в loader_gen*.conf

conf/loader_gen1.conf

## # conf/loader_gen1.conf ## # # Параметры для второго варианта загрузки - GENERIC ядро и образ mfs1. # Параметры, установленные в loader.conf можно заново не указывать  mfs_name="/mfs1"  tmpfs_load="YES" #geom_eli_load="YES"  exec=".( Loading GENERIC kernel and mfs1 root image ) cr"  kernel="kernel"		# /boot sub-directory containing kernel and modules bootfile="kernel"	# Kernel name (possibly absolute path) kernel_options=""	# Flags to be passed to the kernel 

conf/loader_gen2.conf

## # conf/loader_gen2.conf ## # # Параметры для третьего варианта загрузки - GENERIC ядро и образ mfs2. # Параметры, установленные в loader.conf можно заново не указывать  mfs_name="/mfs2"  tmpfs_load="YES" #geom_eli_load="YES"  exec=".( Loading GENERIC kernel and mfs2 root image ) cr"  kernel="kernel"		# /boot sub-directory containing kernel and modules bootfile="kernel"	# Kernel name (possibly absolute path) kernel_options=""	# Flags to be passed to the kernel 

Переходим к загрузочному меню. Поверхностное изучение файлов настроек vanilla/boot/*.4th с конструкциями языка forth вызвало печаль и уныние — к чему все эти вывернутые конструкции, почему же нельзя было написать что-нибудь простое и понятное, например:

ПРОЦ СТАРТ();
ВКЛ МОДУЛЬ ЗАГЛМЕНЮ;
РИСОВАТЬ(«РАМКА1»);
ВЫВОД: «Welcome to caBSD»;

ВЫКЛ МОДУЛЬ ЗАГЛМЕНЮ;
КНЦ;

Эээх…

К счастью, добрые люди (да воздастся им при следующих реинкарнациях) положили в /usr/share/examples/bootforth простые примеры, незначительно изменив которые получились следующие loader.rc и menuconf.4th:

conf/loader.rc

\  \ conf/loader.rc \  \ Example of the file which is automatically loaded by /boot/loader on startup. \ $FreeBSD: releng/10.1/share/examples/bootforth/loader.rc 87636 2001-12-11 00:49:34Z jhb $  include /boot/loader.4th  s" /boot/screen.4th" O_RDONLY fopen dup fload fclose  s" /boot/frames.4th" O_RDONLY fopen dup fload fclose  s" /boot/menuconf.4th" O_RDONLY fopen dup fload fclose  initialize drop  cr main_menu  

conf/menuconf.4th

\ \ conf/menuconf.4th \  \ Simple greeting screen, presenting basic options. \ XXX This is far too trivial - I don't have time now to think about something more fancy... :-/ \ $FreeBSD: releng/10.1/share/examples/bootforth/menuconf.4th 65480 2000-09-05 16:30:09Z dcs $  : title 	f_single \ Координаты вида - w h x y 	60 11 10 4 box 	30 4 at-xy 	."   Welcome to caBSD  " 	me ;  : menu 	2 fg 	15 7 at-xy  	." 1.  Start caBSD kernel and first root image (auto)"         15 8 at-xy 	." 2.  Start GENERIC kernel and first root image" 	15 9 at-xy         ." 3.  Start GENERIC kernel and second root image" 	15 10 at-xy 	." 4.  Reboot" 	me ;  : tkey	( d -- flag | char ) 	seconds + 	begin 1 while 	    dup seconds u< if 		drop 		-1 		exit 	    then 	    key? if 		drop 		key 		exit 	    then 	repeat ;  : prompt 	14 fg 	15 13 at-xy 	." Enter your option (1,2,3,4): " 	10 tkey 	dup 32 = if 	    drop key 	then 	dup 0< if \ Дефолтным по таймауту стартует первый пункт меню. 	    drop 49 	then 	dup emit 	me ;  : help_text         10 17 at-xy ." * Choose 1 to proceed with standard bootstrapping." 	10 18 at-xy ." * Choose 2 or 3 to run special configuration file." 	10 19 at-xy ." * Choose 4 in order to warm boot your machine." 	10 21 at-xy ." * Anyway you have few seconds to interrupt boot," 	10 22 at-xy ."   change parameters and type 'boot' to continue" ;  : (reboot) 0 reboot ;  : main_menu 	begin 1 while 		clear 		f_double 		79 23 1 1 box 		title 		menu 		help_text 		prompt 		cr cr cr 		dup 49 = if 			drop 			1 25 at-xy cr 			." Proceeding with standard boot. Please wait..." cr 			0 boot-conf exit 		then 		dup 50 = if 			drop 			1 25 at-xy cr 			." Loading /boot/loader_gen1.conf. Please wait..." cr 			s" /boot/loader_gen1.conf" read-conf 			0 boot-conf exit 		then 		dup 51 = if 			drop 			1 25 at-xy cr 			." Loading /boot/loader_gen2.conf. Please wait..." cr 			s" /boot/loader_gen2.conf" read-conf 			0 boot-conf exit 		then 		dup 52 = if 			drop 			1 25 at-xy cr 			['] (reboot) catch abort" Error rebooting" 		then 		15 12 at-xy 		." Key " emit ."  is not a valid option!" 		15 13 at-xy 		." Press any key to continue..." 		key drop 	repeat ; 

В результате у нас нарисовалось такое загрузочное меню:

Продолжаем установку. Забираем всё что может пригодиться из ${WORKDIR}/vanilla/boot

# Копируем загрузчик, скрипты и .4th файлы оптом (все они используют друг друга, кроме beastie.4th и brand.4th - их можно удалить) $ cp -p ${WORKDIR}/vanilla/boot/{loader,loader.help,*.rc,*.4th} ${WORKDIR}/custom/boot $ cp -p ${WORKDIR}/vanilla/boot/defaults/loader.conf ${WORKDIR}/custom/boot/defaults $ rm -f ${WORKDIR}/custom/boot/{beastie.4th,brand.4th}  # Устанавливаем наши файлы loader*.conf из conf/ в boot $ install -m 0644 -o root -g wheel conf/{loader,loader_gen1,loader_gen2}.conf ${WORKDIR}/custom/boot/ $ install -m 0644 -o root -g wheel conf/{loader.rc,menuconf.4th} ${WORKDIR}/custom/boot/  # Флешку будем разбивать с GPT, поэтому часть файлов с загрузчиками сохраняем. $ cp -p ${WORKDIR}/vanilla/boot/{pmbr,gptboot,cdboot} ${WORKDIR} 

Несколько слов про модули ядра. Наше ядро caBSD мы компилировали с опцией «makeoptions NO_MODULES=1», а все необходимые модули интегрировали в ядро. Но с GENERIC чуть посложнее. Умное ядро умеет не только автоматически загружать необходимые модули, но и выгружать их. Так, в случае с модулем geom_eli.ko, даже если мы укажем в boot/loader_gen1.conf параметр ‘geom_eli_load=«YES»’, то ядро загрузит его, но потом за ненадобностью выгрузит. Во всяком случае при нашем сценарии шифровании раздела. И когда ядро раскрутит mfs-образ системы, и загрузка дойдёт до скрипта mfs1:/etc/rc.d/geli, модуль будет уже выгружен. А загрузить его заново ядро не сможет — каталог /boot/kernel будет уже невидим изнутри образа mfs1:/.

Навскидку, есть несколько вариантов:

  1. Модули, используемые ядром при загрузке размещать в ${WORKDIR}/custom/boot/kernel, а используемые уже после запуска init, rc-скриптами — в ${WORKDIR}/vanilla/boot/modules (см. параметр ‘module_path="/boot/modules"’ в boot/defaults/loader.conf)
  2. (Умозрительный). Прописать в каком-нибудь rc-скрипте (выполняемом ранее etc/rc.d/geli) монтирование физического носителя (iso/img) и подмонтирование реального каталога /boot/kernel/ с модулями в mfs1:/boot/modules

Но проще всего выглядит первый способ. Реализуем его.

# Устанавливаем 'загрузочные' модули для GENERIC ядра. Необходимость модуля выясняется # чаще всего опытным путём. Как минимум, tmpfs.ko нужен для монтирования mfs образа $ cp -p ${WORKDIR}/vanilla/tmp/boot/kernel/tmpfs.ko ${WORKDIR}/custom/boot/kernel $ strip ${WORKDIR}/custom/boot/kernel/tmpfs.ko  # Удаляем не нужный теперь /boot, куда был сделан installkernel первого ядра $ rm -rf ${WORKDIR}/vanilla/boot  # И создаём пустой каталог для модулей $ mkdir -p ${WORKDIR}/vanilla/boot/modules  # Устанавливаем 'послезагрузочные' модули - geom_eli.ko и зависимый от него crypto.ko $ cp -p ${WORKDIR}/vanilla/tmp/boot/kernel/{geom_eli,crypto}.ko ${WORKDIR}/vanilla/boot/modules $ strip ${WORKDIR}/vanilla/boot/modules/{geom_eli,crypto}.ko   # Удаляем /boot, куда мы скопировали второе ядро и тучу модулей $ rm -rf ${WORKDIR}/vanilla/tmp/boot  # Уменьшаем размеры ядер $ ls -l ${WORKDIR}/custom/boot/{kernel,kernel.cabsd}/kernel $ strip ${WORKDIR}/custom/boot/{kernel,kernel.cabsd}/kernel $ strip --remove-section=.note --remove-section=.comment ${WORKDIR}/custom/boot/{kernel,kernel.cabsd}/kernel  # Сжимаем ядра # freebsd-update не сможет обновить сжатое GENERIC ядро, поэтому будем иметь в виду - # перед обновлением его надо будет разархивировать. Или не сжимать тут изначально. Да. $ gzip -9 -f ${WORKDIR}/custom/boot/kernel.cabsd/kernel # kgzip -o ${WORKDIR}/custom/boot/kernel.cabsd/kernel ${WORKDIR}/custom/boot/kernel.cabsd/kernel # gzip -9 -f ${WORKDIR}/custom/boot/kernel/kernel $ ls -l ${WORKDIR}/custom/boot/{kernel,kernel.cabsd}/kernel* 

Небольшое замечание по сжатию. Ядра мы можем сжать с помощью gzip либо kgzip. В обоих случаях декомпрессия при загрузке происходит автоматически, без каких-то дополнительных настроек.

Для нашего маленького ядра caBSD результаты выглядят так: несжатое ядро — 3.9 Мб, сжатое этими утилитами — по 1.8 Мб, причём kgzip сжал на несколько килобайт эффективнее, но за счёт ~9 Кб загрузчика проиграл gzip.

Для GENERIC ядра немного поинтереснее: несжатое ядро — 17 Мб, сжатое gzip — 7.5 Мб, kgzip — 6.8 Мб. Заметно лучше. И это после удаления символов из ядра. Если их не удалять, то соотношение будет 19: 8.2: 6.8 Мб. Но это так, справочно. Всё равно мы GENERIC ядро из-за потенциальных обновлений сжимать не будем. Кстати, из-за них же можно и не применять strip для этого ядра.

Удаляем лишнее

Несмотря на заметную минимизацию нашей системы за счёт использования опций при компиляции мира, есть ещё много мест, куда мы можем приложить наши руки. И сделаем мы следующее:

  1. Укажем в файлах tools/files2delete_* файлы и каталоги, которые необходимо удалить
  2. Укажем в файле tools/files2keep исключения из п. 1 — файлы, которые необходимо сохранить
  3. Архивируем файлы из п. 2
  4. Удалим файлы и каталоги из п. 1
  5. Восстановим сохранённые файлы из архива

Очевидные плюсы от такой многоходовой комбинации:

  • Можем реализовать схему «удалить всё, кроме» (удалить весь usr/share/locale, кроме ru_RU.UTF-8)
  • При архивации файлов, если файл является символьной ссылкой, то в архив он попадёт как реальный файл. То есть мы можем для files2keep указать каталог /usr/share/locale/ru_RU.UTF-8 и не забивать голову тем, что ссылка на LC_COLLATE из него ведёт в ../la_LN.US-ASCII, LC_CTYPE ведёт в ../UTF-8, а LC_NUMERIC ведёт в ../ru_RU.CP866 — все ссылки сохранятся, а потом и восстановятся как реальные файлы

Сохраняемые файлы

tools/files2keep

# # tools/files2keep # etc/rc.d/DAEMON etc/rc.d/FILESYSTEMS etc/rc.d/LOGIN etc/rc.d/NETWORKING etc/rc.d/SERVERS etc/rc.d/adjkerntz etc/rc.d/devfs etc/rc.d/dmesg etc/rc.d/geli etc/rc.d/geli2 etc/rc.d/gptboot etc/rc.d/hostname etc/rc.d/initrandom etc/rc.d/ldconfig etc/rc.d/mountcritlocal etc/rc.d/postrandom etc/rc.d/random etc/rc.d/root etc/rc.d/securelevel etc/rc.d/syscons etc/rc.d/sysctl etc/pam.d/login #etc/pam.d/other etc/pam.d/passwd etc/pam.d/system usr/libexec/getty usr/share/locale/en_US.UTF-8 usr/share/locale/ru_RU.UTF-8 #usr/share/misc/init.ee usr/share/misc/termcap usr/share/vt/keymaps/ru.kbd usr/share/vt/keymaps/ru.shift.kbd usr/share/vt/keymaps/ru.win.kbd usr/share/zoneinfo/Asia/Yekaterinburg usr/share/zoneinfo/Europe/Moscow usr/share/zoneinfo/UTC usr/local/etc/joe/syntax/conf.jsf usr/local/etc/joe/syntax/csh.jsf usr/local/etc/joe/syntax/diff.jsf usr/local/etc/joe/syntax/sh.jsf 

На самом деле ценного очень мало. Здесь мы сохраняем:

  • нужные нам rc-скрипты запуска из etc/rc.d
  • актуальные в данном случае файлы конфигурации для PAM
  • getty из usr/libexec
  • две локали
  • установленный нами минимальный termcap из conf/termcap
  • все варианты переключения клавиатуры на русский
  • файлы актуальных для нас временных зон
  • несколько файлов настроек для редактора jupp

Теперь составим списки удаляемых файлов и каталогов. Пустые строки, равно как и строки с первым символом ‘#’ игнорируются.

tools/files2delete_bin

# # tools/files2delete_bin # bin/chio bin/domainname bin/ed bin/pax sbin/bsdlabel sbin/camcontrol sbin/ccdconfig sbin/comcontrol sbin/ddb sbin/dhclient sbin/dhclient-script sbin/dump sbin/dumpon sbin/etherswitchcfg sbin/fdisk sbin/fsirand sbin/gbde sbin/ggatec sbin/ggated sbin/ggatel sbin/gvinum sbin/hastctl sbin/hastd sbin/ifconfig sbin/iscontrol sbin/kldconfig sbin/kldunload sbin/mksnap_ffs sbin/mount_fusefs sbin/mount_nfs sbin/nfsiod sbin/nos-tun sbin/nvmecontrol sbin/ping sbin/resolvconf sbin/restore sbin/route sbin/savecore sbin/sconfig sbin/setkey sbin/spppcontrol sbin/swapctl usr/bin/asa usr/bin/banner usr/bin/brandelf usr/bin/chat usr/bin/chfn usr/bin/chkey usr/bin/colldef usr/bin/cpasswd usr/bin/cpuset usr/bin/crontab usr/bin/csup usr/bin/ctlstat usr/bin/dialog usr/bin/dtc usr/bin/ee usr/bin/elf2aout usr/bin/elfdump usr/bin/fetch usr/bin/file usr/bin/finger usr/bin/ftp usr/bin/gcore usr/bin/gencat usr/bin/ipcrm usr/bin/ipcs usr/bin/iscsictl usr/bin/keylogin usr/bin/keylogout usr/bin/ktrace usr/bin/ktrdump usr/bin/lam usr/bin/lastcomm usr/bin/leave usr/bin/lockf usr/bin/look usr/bin/lorder usr/bin/m4 usr/bin/mandoc usr/bin/mesg usr/bin/mkdep usr/bin/mkfifo usr/bin/mklocale usr/bin/mt usr/bin/netstat usr/bin/newgrp usr/bin/nfsstat usr/bin/pagesize usr/bin/passwd usr/bin/pr usr/bin/protect usr/bin/rctl usr/bin/revoke usr/bin/rpcinfo usr/bin/rs usr/bin/rup usr/bin/rusers usr/bin/rwall usr/bin/send-pr usr/bin/showmount usr/bin/smbutil usr/bin/sockstat usr/bin/stdbuf usr/bin/su usr/bin/tabs usr/bin/talk usr/bin/tcopy usr/bin/tee usr/bin/tftp usr/bin/tip usr/bin/tsort usr/bin/units usr/bin/unvis usr/bin/vis usr/bin/vmstat usr/bin/wall usr/bin/what usr/bin/whois usr/bin/write usr/sbin/adduser usr/sbin/arp usr/sbin/asf usr/sbin/automountd usr/sbin/binmiscctl usr/sbin/boot0cfg usr/sbin/bootparamd usr/sbin/bootpef usr/sbin/bootptest usr/sbin/bsdconfig usr/sbin/bsdinstall usr/sbin/callbootd usr/sbin/cdcontrol usr/sbin/chroot usr/sbin/ckdist usr/sbin/clear_locks usr/sbin/cpucontrol usr/sbin/crashinfo usr/sbin/cron usr/sbin/ctladm usr/sbin/ctld usr/sbin/dconschat usr/sbin/digictl usr/sbin/dumpcis usr/sbin/etcupdate usr/sbin/fifolog_create usr/sbin/fifolog_reader usr/sbin/fifolog_writer usr/sbin/fwcontrol usr/sbin/getfmac usr/sbin/getpmac usr/sbin/i2c usr/sbin/ifmcstat usr/sbin/inetd usr/sbin/iscsid usr/sbin/keyserv usr/sbin/kgmon usr/sbin/kgzip usr/sbin/lptcontrol usr/sbin/memcontrol usr/sbin/mergemaster usr/sbin/mfiutil usr/sbin/mixer usr/sbin/mlxcontrol usr/sbin/mount_smbfs usr/sbin/mountd usr/sbin/moused usr/sbin/mptable usr/sbin/mptutil usr/sbin/mtest usr/sbin/newsyslog usr/sbin/nfscbd usr/sbin/nfsd usr/sbin/nfsdumpstate usr/sbin/nfsrevoke usr/sbin/nfsuserd usr/sbin/nmtree usr/sbin/nologin usr/sbin/pciconf usr/sbin/periodic usr/sbin/powerd usr/sbin/procctl usr/sbin/quot usr/sbin/rarpd usr/sbin/rmt usr/sbin/rmuser usr/sbin/rpc.lockd usr/sbin/rpc.statd usr/sbin/rpc.umntall usr/sbin/rpcbind usr/sbin/rtprio usr/sbin/services_mkdb usr/sbin/setfib usr/sbin/setfmac usr/sbin/setpmac usr/sbin/sicontrol usr/sbin/smbmsg usr/sbin/snapinfo usr/sbin/spkrtest usr/sbin/spray usr/sbin/syslogd usr/sbin/sysrc usr/sbin/tcpdchk usr/sbin/tcpdmatch usr/sbin/tcpdrop usr/sbin/tcpdump usr/sbin/timed usr/sbin/timedc usr/sbin/traceroute usr/sbin/trpt usr/sbin/ugidfw usr/sbin/vipw usr/sbin/wake usr/sbin/watch usr/sbin/watchdog usr/sbin/zdump usr/sbin/zic usr/sbin/zzz # vi usr/bin/vi 

Чистим bin, sbin, usr/bin, usr/sbin. При удалении файлов автоматически ищутся и удаляются жёсткие ссылки на них.

tools/files2delete_etc

# # tools/files2delete_etc # etc/X11 etc/auto_master etc/autofs etc/bluetooth etc/crontab etc/ddb.conf etc/defaults/bluetooth.device.conf etc/defaults/periodic.conf etc/devd etc/devd.conf etc/dhclient.conf etc/disktab etc/dumpdates etc/ftpusers etc/gss etc/hosts etc/hosts.allow etc/hosts.equiv etc/inetd.conf etc/libalias.conf etc/mac.conf etc/mail etc/mtree etc/netconfig etc/netstart etc/networks etc/newsyslog.conf etc/newsyslog.conf.d etc/nsmb.conf etc/ntp etc/pam.d #etc/pam.d/passwd etc/pccard_ether etc/periodic etc/phones etc/pkg etc/ppp etc/rc.bsdextended etc/rc.d etc/rc.firewall etc/rc.initdiskless etc/rc.resume etc/rc.sendmail etc/rc.suspend etc/remote etc/rpc etc/security etc/skel etc/ssh etc/ssl etc/syslog.conf etc/termcap.small etc/zfs 

Чистим etc. Обратите внимание — удаляется etc/rc.d целиком, а нужные rc-скрипты запуска потом восстановятся из чуть ранее созданного архива.

tools/files2delete_lib

# # tools/files2delete_lib # lib/libalias.so.7 lib/libalias_cuseeme.so lib/libalias_dummy.so lib/libalias_ftp.so lib/libalias_irc.so lib/libalias_nbt.so lib/libalias_pptp.so lib/libalias_skinny.so lib/libalias_smedia.so lib/libbegemot.so.4 lib/libcam.so.6 lib/libgcc_s.so.1 lib/libipsec.so.4 lib/libpcap.so.8 lib/libreadline.so.8 lib/libthr.so.3 lib/libulog.so.0 libexec/resolvconf # OpenSSL IBM 4758 CCA hardware engine support usr/lib/engines/lib4758cca.so # OpenSSL AEP hardware engine support usr/lib/engines/libaep.so # OpenSSL Atalla hardware engine support usr/lib/engines/libatalla.so # OpenSSL CHIL hardware engine support usr/lib/engines/libchil.so # OpenSSL CryptoSwift hardware engine support usr/lib/engines/libcswift.so # OpenSSL Nuron hardware engine support usr/lib/engines/libnuron.so # OpenSSL SureWare hardware engine support usr/lib/engines/libsureware.so # OpenSSL UBSEC hardware engine support usr/lib/engines/libubsec.so usr/lib/private usr/lib/libBlocksRuntime.so.0 usr/lib/libalias.so usr/lib/libauditd.so.5 usr/lib/libbegemot.so usr/lib/libbsm.so.3 usr/lib/libcam.so usr/lib/libcurses.so usr/lib/libcursesw.so usr/lib/libdwarf.so.3 usr/lib/libexecinfo.so.1 usr/lib/libfetch.so.6 usr/lib/libform.so.5 usr/lib/libformw.so.5 usr/lib/libgcc_s.so usr/lib/libgomp.so.1 usr/lib/libhistory.so.8 usr/lib/libipsec.so usr/lib/libmagic.so.4 usr/lib/libmemstat.so.3 usr/lib/libmenu.so.5 usr/lib/libmenuw.so.5 usr/lib/libpanel.so.5 usr/lib/libpanelw.so.5 usr/lib/libpcap.so usr/lib/libproc.so.2 usr/lib/libpthread.so usr/lib/libradius.so.4 usr/lib/libreadline.so usr/lib/librt.so.1 usr/lib/librtld_db.so.2 usr/lib/libsmb.so.4 usr/lib/libstdbuf.so.1 usr/lib/libstdthreads.so.0 usr/lib/libtacplus.so.5 usr/lib/libtermcap.so usr/lib/libtermcapw.so usr/lib/libtermlib.so usr/lib/libtermlibw.so usr/lib/libthr.so usr/lib/libthread_db.so.3 usr/lib/libtinfo.so usr/lib/libtinfow.so usr/lib/libugidfw.so.4 usr/lib/libutempter.so usr/lib/libvgl.so.6 usr/lib/libwrap.so.6 usr/lib32 usr/libdata usr/libexec 

Бибилиотеки. Самый ответственный файл. PAM-модули из usr/lib вынесены в отдельный файл

tools/files2delete_local

# # tools/files2delete_local # usr/local/libdata usr/local/man usr/local/share usr/local/etc/joe/charmaps usr/local/etc/joe/syntax 

Если ставили пакеты, то можно почистить usr/local

tools/files2delete_pam

# # tools/files2delete_pam # usr/lib/pam_chroot.so usr/lib/pam_deny.so usr/lib/pam_echo.so usr/lib/pam_exec.so usr/lib/pam_ftpusers.so usr/lib/pam_group.so usr/lib/pam_guest.so #usr/lib/pam_lastlog.so #usr/lib/pam_login_access.so #usr/lib/pam_nologin.so #usr/lib/pam_opie.so #usr/lib/pam_opieaccess.so usr/lib/pam_passwdqc.so usr/lib/pam_permit.so usr/lib/pam_radius.so usr/lib/pam_rhosts.so usr/lib/pam_rootok.so #usr/lib/pam_securetty.so #usr/lib/pam_self.so usr/lib/pam_tacplus.so #usr/lib/pam_unix.so 

PAM-модули.

tools/files2delete_var

# # tools/files2delete_var # var/account var/at var/audit var/authpf var/crash var/cron var/db/hyperv var/db/ipf var/db/pkg var/db/ports var/db/portsnap var/games var/heimdal var/mail var/msgs var/run/ppp var/run/wpa_supplicant var/rwho var/spool var/unbound var/yp 

Можно почитать man hier и почистить иерархию var от ненужных пустых каталогов. Для красоты.

tools/files2delete_other

# # tools/files2delete_other # usr/games usr/include usr/share/bsdconfig usr/share/dtrace usr/share/examples usr/share/info # keys? usr/share/keys usr/share/locale usr/share/man usr/share/mdocml usr/share/misc usr/share/nls usr/share/openssl usr/share/skel usr/share/tabset usr/share/vi usr/share/vt/keymaps usr/share/zoneinfo 

Чистим всё остальное, в основном usr/share

Стоит отметить:

  • пути к файлам необходимо записывать аккуратно, без ведущего слеша, чтобы не выхолостить базовую систему
  • никакой проверки на пробелы, кавычки и тому подобное в именах файлов не делается
  • смысл удаления не столько в экономии места на флешке, да и при создании второго mfs образа мы щедрой рукой накинем десяток-другой мегабайт под место для обновлений, сколько в том, чтобы просто удалить явно ненужное, оставив необходимое (да-да, ядро, шелл и openssl), ну и то, что может пригодится — а это самое творческое. Впрочем, при желании можно сделать две флешки — одну действительно минимальную, а вторую — с инструментарием

Приступаем.

# Удаляем файл архива, если он остался от предыдущих итераций $ rm -f keepfiles.tar  # Архивируем файлы, перечисленные в tools/files2keep $ tar --create --file keepfiles.tar --directory ${WORKDIR}/vanilla --dereference --files-from tools/files2keep  # Удаляем файлы из списка и их жёсткие ссылки. # Если оформлять в виде скрипта, то нагляднее было бы сделать циклом for, да с дополнительными проверками, но циклы # неудобно копипастить в командную строку, поэтому в данном примере обойдёмся такими вот однострочными конструкциями: $ cat tools/files2delete_bin   | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla -samefile ${WORKDIR}/vanilla/% -exec rm -rf {} \;  # Далее удаляем без -samefile $ cat tools/files2delete_etc   | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \; $ cat tools/files2delete_local | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \; $ cat tools/files2delete_other | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \; $ cat tools/files2delete_pam   | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \; $ cat tools/files2delete_lib   | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \; $ cat tools/files2delete_var   | egrep -v '(^#|^\s*$)' | xargs -I % -t -L 1 find ${WORKDIR}/vanilla/% -exec rm -rf {} \;  # Восстанавливаем заботливо сохранённое $ tar --extract --file keepfiles.tar --verbose --directory ${WORKDIR}/vanilla $ rm -f keepfiles.tar 

Небольшой экскурс в историю — как получились эти файлы.

Были изучены rc-скрипты запуска в etc/rc.d, отобраны необходимые и прописаны в список для сохранения. Затем сформирован небольшой список к удалению, включающий в себя строчку ‘etc/rc.d’ и, в общем-то, содержимое нынешнего files2delete_other. Ключевым моментом было удалить все лишние скрипты запуска. Затем создали флешку, загрузились с неё и запустили поиск файлов:

$ find / -type f -amin -15 > /ca.certs/files-keep $ find / -type f -not -amin -15 > /ca.certs/files-delete 

Так были получены списки с «холодными» файлами и «горячими», использовавшимися в процессе загрузки, чей atime изменился за последние пятнадцать минут. Если бы мы не почистили заранее rc-скрипты, то в список «горячих» файлов попали бы многие явно лишние — типа ifconfig и тому подобные.

Ещё раз поиск файлов был произведён после обновления системы с помощью freebsd-update — это позволило выявить необходимые для её работы программы, порой неочевидные, типа join, tr, touch.

Из перечня «холодных» файлов сформировали списки:

  • files2delete_bin — отсюда исключались программы, которые могут пригодиться — для манипуляций с текстом, дисковые утилиты, архиваторы и прочие. Предварительно был получен список файлов с жёсткими ссылками («cd ${WORKDIR}/vanilla && find bin/ -type f -links +1 -exec echo „hardlinks of “ {} \; -exec find bin/ -samefile {} \;», или более удобно с помощью sysutils/samefile) — он использовался для избежания как включения одной ипостаси программы в список на удаление, а второй — на сохранение, так и многократного включения жёстких ссылок на один и тот же файл
  • files2delete_etc — тут исключались скрипты, используемые при завершении работы и файлы типа /etc/passwd, etc/master.passwd
  • files2delete_local — файл формировался с учетом установленных программ из портов — clex и jupp
  • files2delete_other — всё ценное сохранили с помощью files2keep, остальное было признано ненужным
  • files2delete_var — man hier и вперёд — rm -rf, rm -rf
  • files2delete_lib — после формирования списка files2delete_bin и применения его в ${WORKDIR}/vanilla, на изрядно похудевшие каталоги с программами был напущен найденный на просторах интернета скрипт по поиску «неиспользуемых» библиотек (ldd + grep), выдавший список, который после некоторой доработки составил этот файл. PAM модули были перенесены в другой файл. Так же не стали включать библиотеки из /lib/geom/. Причём, после первоначальной чистки библиотек, тот скрипт был запущен ещё раз, что помогло выявить ещё несколько библиотек, видимо находившихся в зависимостях у только что удалённых (libulog, libmagic, libwrap)
  • files2delete_pam — скриптом PAM модули показываются как неиспользуемые, хотя в действительности они нужны. Поэтому пришлось чистить файлы конфигурации в etc/pam.d/, оставлять модули, упомянутые в сохранённых файлах (login, passwd, system) и смириться с очевидными ограничениями — так, удалив etc/pam.d/su — мы не сделаем su. А с другой стороны — зачем нам нужен su, если по Alt+F1 у нас будет консоль ‘ca’, по Alt+F2 у нас ‘root’

По итогам чистки системы была сделана флешка, в которой последовательно запускали все файлы из bin, sbin, usr/bin, usr/sbin на предмет выявления грубых ошибок. Обнаружилось, что poweroff пытается вызвать wall для оповещения пользователей; clear хочет tput (команда нужная, tput пришлось вернуть в систему); а костыль для whereis мы предусмотрели раньше. Конечно, выдача «usage:» не требовала от программ использования каких-то неочевидных библиотек, но зато можно было в отчётности отметить что «всё запускается». Ну а дальше, практика — критерий истины.

Создаём образ системы

Производим заключительную чистку ${WORKDIR}/vanilla

# Удаляем пустые каталоги в /usr. $ find ${WORKDIR}/vanilla/usr -type d -empty -delete  # Удаляем мёртвые символьные ссылки $ chroot ${WORKDIR}/vanilla find -L / -type l -exec rm -f {} \;  # Удаляем скачанные пакеты $ rm -rf ${WORKDIR}/vanilla/tmp/pkg.files  # Удаляем БД pkg $ rm -rf ${WORKDIR}/pkg.db  # Удаляем БД по установленным пакетам $ rm -f ${WORKDIR}/vanilla/var/db/pkg/local.sqlite  # Создаём termcap db $ chroot ${WORKDIR}/vanilla cap_mkdb /usr/share/misc/termcap  # Программа 'nologin' у нас была удалена при чистке - уж больно большая # она, аж 372 кб, поэтому создаём одномённый симлинк на 'false' # При очередном freebsd-update файл восстановится. Но это грозит лишь второму образу системы $ ln -f ${WORKDIR}/vanilla/usr/bin/false ${WORKDIR}/vanilla/usr/sbin/nologin  # Создаём точки монтирования для ЦС $ mkdir -p -m 0700 ${WORKDIR}/vanilla/{ca,ca.keys,ca.certs} # и для флешек $ mkdir -p -m 0700 ${WORKDIR}/vanilla/home/ca/mnt # Устанавливаем владельца $ chroot ${WORKDIR}/vanilla chown ca:ca /ca /ca.keys /ca.certs /home/ca/mnt  # Создаём mfs образы. Один (эталонный) мы создадим с минимумом свободного места, а для второго добавим # запас, на случай обновлений freebsd-update. Параметры к makefs будем использовать следующие: # -b free-blocks - Ensure that a minimum of 'free-blocks' free blocks (512 bytes) exist in the image # -f free-files - Ensure that a minimum of 'free-files' free files (inodes) exist in the image # -o fs-options - Set file system specific options # -t fs-type - Create an 'fs-type' file system image  # Первый образ. Добавляем 1 Мб свободного места, чтобы никакие утилиты не чувствовали себя ущемлёнными $ makefs -b 1m  -f 100 -o label=sysimg1 -t ffs ${WORKDIR}/custom/mfs1 ${WORKDIR}/vanilla  # Он меняться не будет, сожмём его. $ gzip -9 -f ${WORKDIR}/custom/mfs1  # Второй образ. Предусмотрим 10 Мб свободного места для обновления системы. $ makefs -b 10m -f 100 -o label=sysimg2 -t ffs ${WORKDIR}/custom/mfs2 ${WORKDIR}/vanilla 

Создаём CD

Практическая польза от записи на CD-ROM сомнительна — всё же флешки практичнее, но этот образ очень удобно использовать для тестирования предыдущих этапов конструирования нашей системы, особенно загрузки. Сделали iso образ, подсунули его тому же qemu, попытались загрузиться — посмотрели, что да как.

$ makefs -t cd9660 -o bootimage=i386\;${WORKDIR}/cdboot,label=caBSD,no-emul-boot,rockridge caBSD-10.1-i386.iso ${WORKDIR}/custom  # Готово $ ls -l caBSD-10.1-i386.iso 

Создаём флешку

Вот мы и подошли к заключительному этапу — созданию img образа для записи на флешку.

У нас будет следующая структура:

  1. Раздел для обмена с внешними системами — для записи .csr и выгрузки .crt, тип ms-basic-data, размер 1 Мб. Делаем его первым, чтобы без дополнительных телодвижений был виден в Windows (7, на XP и ниже не проверялся)
  2. Загрузочный GPT раздел, freebsd-boot, 64 Кб
  3. Основной раздел с /boot, ядрами и mfs-образами, freebsd-ufs, размером с содержимое ${WORKDIR}/custom
  4. Раздел со скриптами и файлами конфигурации нашего ЦС, freebsd-ufs, 1 Мб
  5. Шифрованный раздел с секретными ключами ЦС, freebsd-ufs, 1 Мб. Сильно маленьким лучше не делать, т.к. geli может съесть прилично места
# Удаляем результаты предыдущих опытов $ rm -f caBSD-10.1-i386.img caBSD-10.1-i386.txt  # Устанавливаем в переменные окружения сохранённые ранее цифровые uid/gid пользователя 'ca' # На предыдущем шаге мы свернули корневой раздел нашей новой системы в файл, делать chroot уже некуда, # поэтому устанавливать владельцем файлов пользователя 'ca' будем по его цифровому id $ set CA_UID=`cat ${WORKDIR}/ca.uid` $ set CA_GID=`cat ${WORKDIR}/ca.gid`  # Получаем размер ${WORKDIR}/custom (в Мб). $ set SYSSIZE=`du -sm ${WORKDIR}/custom | cut -f 1`  # Размер основного раздела. Опытным путём возникла цифра в три мегабайта, # которые мы добавляем на служебные области UFS и прочие доли ангелов. $ set SYSSIZE=`expr ${SYSSIZE} + 3`  # Добавляем резерв под обновление ядра. Несжатое GENERIC ядро весит 17 Мб. Для обновления freebsd-update # нужно это место учесть. А если включите "BackupKernel yes" в freebsd-update.conf, то и под предыдущее # ядро. Либо можно при обновлении монтировать /boot/kernel в tmpfs, там разархивировать, обновлять, # архивировать и класть на место. Но пока добавим 30 Мб на всё. Для тестирования $ set SYSSIZE=`expr ${SYSSIZE} + 30`  # Размер файла с образом для флешки (system + exchange + ca + keys) $ set IMGSIZE=`expr ${SYSSIZE} + 1 + 1 + 1`  # Создаём пустой пока img образ $ dd if=/dev/zero of=caBSD-10.1-i386.img count=${IMGSIZE} bs=1m  # Монтируем его как md устройство $ set MDDEVICE=`mdconfig -a -t vnode -f caBSD-10.1-i386.img` 

Количество создаваемых md устройств в базовой системе не бесконечно, поэтому в случае повторных попыток создания img образа не забывайте удалять устройства, оставшиеся от предыдущих попыток — «ls /dev/md*» и следом повторять «mdconfig -d -u N», подставляя вместо ‘N’ номера ненужных md устройств. И обращать внимание, на что указывает в данный момент ${MDDEVICE}

Создаём разделы

# Создаём разделы, согласно man gpart $ gpart create -s GPT ${MDDEVICE}  # Embed GPT bootstrap code into a protective MBR: $ gpart bootcode -b ${WORKDIR}/pmbr ${MDDEVICE}  # Create a dedicated freebsd-boot partition that can boot FreeBSD from a freebsd-ufs partition, # and install bootstrap code into it. We uses 88 blocks (44 kB) so the next partition will be # aligned on a 64 kB boundary. The boot partition itself is aligned on a 4 kB boundary # Не совсем очевидна необходимость выравнивания по границе 4k - это же не SSD и не жёсткий диск. # Но пусть будет. $ gpart add -b 40 -s 1m          -t ms-basic-data -l exchange ${MDDEVICE} $ gpart add -a 4k -s 64k         -t freebsd-boot  -l boot     ${MDDEVICE} $ gpart add -a 4k -s ${SYSSIZE}m -t freebsd-ufs   -l system   ${MDDEVICE} $ gpart add -a 4k -s 1m          -t freebsd-ufs   -l ca       ${MDDEVICE} $ gpart add -a 4k                -t freebsd-ufs   -l keys     ${MDDEVICE} # Размер последнего раздела не указываем, фактически он будет чуть меньше 1 Мб  # Показываем, что получилось $ gpart show ${MDDEVICE}  # Сохраняем структуру в файл $ gpart backup ${MDDEVICE} > caBSD-10.1-i386.txt  $ unset SYSSIZE IMGSIZE 

Форматируем разделы

# Форматируем первый раздел # Если указать "-F 32", то будет FAT32, если не указывать - автоопределение. # Для нашего размера в 1 Мб - FAT12 $ newfs_msdos -L exchange /dev/gpt/exchange  # Второй раздел. Указываем номер freebsd-boot раздела -> "-i 2" $ gpart bootcode -p ${WORKDIR}/gptboot -i 2 ${MDDEVICE}  # Третий раздел # -O - filesystem type; # -n - disable snapshot. Background fsk must be OFF in rc.conf # -o - optimization; # -m - free-space. The percentage of space reserved from normal users; # Опцию -U не ставим - софтапдейты не нужны, -t тоже не ставим - это не SSD $ newfs -O2 -n -o space -m 0 -L system /dev/gpt/system  # Четвёртый раздел $ newfs -O2 -n -o space -m 0 -L ca /dev/gpt/ca  # Пятый раздел. Создаём шифрованный раздел для закрытых ключей данного ЦС # В данном примере будем обходиться лишь одной парольной фразой. В реальной жизни # можно использовать ключевые файлы (user key + company key)  # Инициализируем раздел /dev/gpt/keys, дважды вводим пароль # -a Enable data integrity verification (authentication) using the given algorithm # -B File name to use for metadata backup # -s Change decrypted provider's sector size. # Здесь мы HMAC включать не будем, с ним хлопот больше. Опять же, если будут повреждены файлы # с ключами - мы об этом узнаем и так, при попытке их использования. К тому же HMAC уменьшает # полезную ёмкость раздела. В зависимости от размеров секторов может даже весьма значительно $ geli init -B caBSD-10.1-i386.gelibak -s 4096 /dev/gpt/keys  # Сохраняем метаданные в текстовый файл, потом распечатаем. На бумаге оно понадёжнее будет. # Коли приспичит, семьсот символов напечатать не сложно. $ cat caBSD-10.1-i386.gelibak | b64encode caBSD-10.1-i386.gelibak >> caBSD-10.1-i386.txt  # Отметим возможность указания параметра '-r' - Attach read-only provider. Тут будет # храниться только закрытый ключи ЦС и после его создания раздел можно будет подключать # в режиме для чтения - параметр geli_default_flags="-r" в rc.conf. И 'ro' в fstab $ geli attach /dev/gpt/keys  # Появившийся шифрованный раздел /dev/gpt/keys.eli мы можем заполнить случайными данными # (dd if=/dev/random of=/dev/gpt/keys.eli bs=4096), а можем и не заполнять, и так ясно что # там хранится. Размер блока (4k) должен быть кратным тому, который мы указали в geli init  # Форматируем (именно keys.eli) $ newfs -O2 -n -o space -m 0 -L gelikeys /dev/gpt/keys.eli  # Позже, загрузившись с нашей флешки и сделав df -h мы увидим:  Filesystem           Size    Used   Avail Capacity  Mounted on /dev/md0              14M     13M    690K    95%    / devfs                1.0K    1.0K      0B   100%    /dev tmpfs                 50M    4.0K     50M     0%    /tmp /dev/gpt/ca          828K    240K    588K    29%    /ca /dev/gpt/keys.eli    720K    4.0K    716K     1%    /ca.keys /dev/gpt/exchange    999K    6.5K    992K     1%    /ca.certs 

Наполняем разделы

# [keys] Начнём с него, чтоб развязаться с geli. Раздел пока пустой, # специально обученные люди ключи потом создадут сами $ mount /dev/gpt/keys.eli ${WORKDIR}/mnt  # Для удобства отладки ставим метку "Здесь был Вася" $ touch ${WORKDIR}/mnt/.created-keys.eli  # Меняем владельца раздела на 'ca' $ chown -R ${CA_UID}:${CA_GID} ${WORKDIR}/mnt  # Отключаем $ umount ${WORKDIR}/mnt $ geli detach /dev/gpt/keys.eli  # [exchange]. Тоже пустой раздел # -m maximum file permissions for files # -M maximum file permissions for directories $ mount_msdosfs -u ${CA_UID} -g ${CA_GID} -m 0640 -M 0750 -l /dev/gpt/exchange ${WORKDIR}/mnt  # Это не UFS раздел, владельца и права каждый раз задаём при монтировании. # Что тут, что в fstab. Поэтому просто ставим метку и размонтируем $ touch ${WORKDIR}/mnt/.created-exchange $ umount ${WORKDIR}/mnt  # [ca] Устанавливаем файлы скриптов и настроек нашего ЦС из архива. # Предположим, он находится в tools/ca.tar.gz. $ mount /dev/gpt/ca ${WORKDIR}/mnt $ tar --extract --gunzip --no-same-permissions --file tools/ca.tar.gz --directory ${WORKDIR}/mnt  # Всем файлам права rw-r----- $ chmod -R u=rw,g=r,o= ${WORKDIR}/mnt/*  # Каталогу conf права rwxr-x--- $ chmod u=rwx,g=rx,o= ${WORKDIR}/mnt/conf  # Скриптам права rwxr-x--- $ chmod u=rwx,g=rx,o= ${WORKDIR}/mnt/*.sh  $ touch ${WORKDIR}/mnt/.created-ca $ chown -R ${CA_UID}:${CA_GID} ${WORKDIR}/mnt $ umount ${WORKDIR}/mnt  # [system] Готовимся заполнять образ системы $ mount /dev/gpt/system ${WORKDIR}/mnt  # Копируем всё $ cp -av ${WORKDIR}/custom/. ${WORKDIR}/mnt/ $ umount ${WORKDIR}/mnt  # Убираем за собой $ mdconfig -d -u ${MDDEVICE} $ unset MDDEVICE CA_UID CA_GID  # Готово $ ls -l caBSD-10.1-i386.img 

Обновление системы

Теперь настроим оффлайновое обновление нашей FreeBSD на флешке. Проще всего при каждом важном обновлении заново создавать флешку по данному руководству, но это слишком легко, не наш путь.

Прежде всего нам понадобится оставленный в живых после создания флешки каталог ${WORKDIR}. Можно, конечно, без него — каждый раз ставить в виртуальной машине чистую систему с DVD — но тогда будут скачиваться все обновления, для всех программ, даже для тех, которые мы в варианте для флешки либо не ставили, либо удаляли. Поэтому будем считать, что каталог ${WORKDIR} у нас есть.

Поиск и скачивание обновлений будет проходить двумя итерациями — отдельно для ядра, отдельно для остальной системы.

Имеет место определённая неоднозначность — freebsd-update получает информацию о текущем ядре из переменной kern.bootfile, то есть базовой системы. Но в нашем случае они идентичны — ядро GENERIC мы как раз брали оттуда.

$ cd ${BASE}  # Разархивируем GENERIC ядро, если сжимали, иначе freebsd-update просто его не найдёт # gunzip ${WORKDIR}/custom/boot/kernel/kernel.gz  # В custom у нас только каталог /boot, поэтому получим обновления только для GENERIC ядра и модулей # -b basedir - Operate on a system mounted at basedir # -d workdir - Store working files in workdir # -f conffile - Read configuration options from conffile $ freebsd-update fetch -b ${WORKDIR}/custom -d ${WORKDIR}/mnt -f ${WORKDIR}/vanilla/etc/freebsd-update.conf  # Скачиваем обновления для остальной системы $ freebsd-update fetch -b ${WORKDIR}/vanilla -d ${WORKDIR}/mnt -f ${WORKDIR}/vanilla/etc/freebsd-update.conf 

К сожалению (?), в ветке FreeBSD 10.1 на момент написания статьи не было обновлений модулей ядра, поэтому проверить как они обновляются не получилось. Соответственно остался открытым вопрос — по каким путям ищутся модули. Возможно, понадобятся ещё какие-то телодвижения для корректного обновления, учитывая, что у нас модули в двух местах.

Итак, обновления мы получили, смотрим в ${WORKDIR}/mnt. Там у нас каталог files с обновлениями, несколько служебных файлов, и две длинных символических ссылки с хвостиком -install. Имена этих ссылок представляют собой SHA256 хеши от имён каталогов "${WORKDIR}/custom" и "${WORKDIR}/vanilla". При установке обновлений программа заново сформирует хеши по именам каталогов, указанных в параметре ‘-b’ и будет искать индексные файлы в /mnt по данным ссылкам. Но устанавливать обновления мы будем на флешке, где разделы с ядром и системой будут монтироваться совсем в другие места, и хеши этих точек монтирования будут немного отличаться. Поэтому сделаем вот так:

$ cd ${WORKDIR}/mnt  # На флешке раздел с ядром будем монтировать в /media/sys # Создадим ссылку на каталог с индексами для ядра $ ln -s `echo ${WORKDIR}/custom | sha256 -q`-install ${WORKDIR}/mnt/`echo /media/sys | sha256 -q`-install  # А раздел с системой - в /media/mfs $ ln -s `echo ${WORKDIR}/vanilla | sha256 -q`-install ${WORKDIR}/mnt/`echo /media/mfs | sha256 -q`-install  # Архивируем $ tar -cvf ${BASE}/update.tar .  $ cd ${BASE} 

Архив переносим на транспортную флешку. Будем считать, что она отформатирована под FAT.

Загружаемся с нашей флешки с caBSD, выбрав третий пункт в загрузочном меню — GENERIC ядро и mfs2 образ. Во втором окне консоли входим как root.

# Нам понадобятся каталоги для точек монтирования, /mnt у нас занят - он прописан # в /etc/freebsd-update.conf как источник обновлений, поэтому воспользуемся /media $ mkdir /media/{sys,mfs,flash}  # Монтируем системный раздел $ mount /dev/gpt/system /media/sys  # в /media/sys нам открылся каталог /boot с ядрами и двумя образами системы. # Теперь монтируем второй образ в /dev/md1 (md0 занято нашим /) $ mdconfig -a -t vnode -f /media/sys/mfs2 -u 1 $ mount /dev/md1 /media/mfs  # Монтируем транспортную флешку с обновлениями в /media/flash $ mount_msdosfs -l /dev/da1s1 /media/flash  # Так как размер обновлений может быть большим, то извлекать их будем в tmpfs # Оцениваем размер обновлений $ du -h /media/flash/update.tar # Монтируем с указанием соответствующего размера $ mount -t tmpfs -o size=100m tmpfs /mnt  # Извлекаем обновления в /mnt $ tar -xvf /media/flash/update.tar -C /mnt  # Разархивируем ядро (если сжимали изначально) # gunzip /media/sys/boot/kernel/kernel.gz  # freebsd-update берёт путь к ядру из переменной ядра, поэтому её корректируем $ sysctl kern.bootfile="/media/sys/boot/kernel/kernel"  # Обновляем ядро $ freebsd-update install -b /media/sys -d /mnt  # Сжимаем ядро. До следующего раза. # Хотя можно и не сжимать - место в образе всё равно выделено # gzip -9 /media/sys/boot/kernel/kernel  # Обновляем мир $ freebsd-update install -b /media/mfs -d /mnt  # Убираем за собой $ umount /media/flash $ umount /mnt $ umount /media/mfs $ mdconfig -d -u 1 $ umount /media/sys $ rmdir /media{sys,mfs}  $ reboot  # Загружаемся, выбрав третий пункт меню  # Проверяем $ freebsd-version  10.1-RELEASE-p12  $ openssl version  OpenSSL 1.0.1l-freebsd 15 Jan 2015 

Всё.

Касательно самого ЦС — дублировать руководства, приводя примеры openssl req …, openssl ca …, и так далее не вижу смысла. Но могу порекомендовать неплохой ресурс pki-tutorial, по ссылке ниже.

Ссылки по теме:

Russian Unicode, wiki.freebsd.org/Newcons и unix1.jinr.ru/~lavr/vt/vtcons.html
Шрифты из склепа — habrahabr.ru/post/137544
OpenSSL PKI Tutorial — pki-tutorial.readthedocs.org
mfsBSD, mfsbsd.vx.sk
EasyBSD, www.fbsd-dev.org
FreeWDE, rop.gonggri.jp/?p=269

ссылка на оригинал статьи http://habrahabr.ru/post/262151/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *