FreeBSD por defecto proporciona una imagen del kernel genérica, esta incluye una gran cantidad de drivers para soportar el mayor número de hardware posible, además incluye las funcionalidades mas ampliamente utilizadas así como deshabilitadas las menos comunes.
Compilando el kernel conseguiremos varios beneficios:
- Mayor rapidez en el arranque del sistema, dado que el kernel sólo tiene que probar el hardware que realmente tiene el equipo.
- Menor consumo de RAM ya que hemos eliminado las partes innecesarias.
- Menor superficie de ataque al eliminar partes del código que ya no estarán presentes.
- Habilitar el soporte para un hardware en concreto que en GENERIC viene deshabilitado.
- Habilitar opciones exóticas que vienen deshabilitada por defecto.
En mi caso la motivación por la que recompilar el kernel viene dada por las ganas de experimentar con una imagen mínima para mi servidor casero.
El manual se compone de distintos apartados:
- Instalación del código fuente del kernel
- Identificación de hardware
- Compilación del kernel
- Updates
- Troubleshooting
Instalación del código fuente del kernel:
Instalamos Git:
Las fuentes del SO se guardan en /usr/src, esto es un dataset ZFS.
NAME USED AVAIL REFER MOUNTPOINT
zroot/usr/src 827M 27.2G 827M /usr/src
Por lo tanto si queremos partir desde cero debemos recrearlo:
zfs destroy zroot/usr/src
zfs create zroot/usr/src
Clonamos el repo en el directorio /usr/src:
La rama principal del repositorio de Git corresponde con la CURRENT , nosotros queremos permanecer en la RELEASE, mas concretamente en la misma versión con la que fueron compiladas las core userlandtools de nuestro sistema, esto nos evitará muchos problemas extraños y difíciles de debugear(como que algunas conexiones de red fallen y otras no).
Consultamos la versión de las userlands:
13.0-RELEASE-p11
Podemos ver las branches en esta web , la mas actual 13.0 RELEASE es:
releng/13.0
Cambiamos a la branch deseada:
git checkout releng/13.0
Con el tiempo querremos actualizar nuestro repositorio para mantener al día el código fuente del kernel, para ello podemos optar por dos métodos.
Manualmente:
git pull --rebase
Mediante freebsd-update:
Tan solo debemos asegurarnos de que esté configurado para actualizar también el código fuente del kernel, por defecto ya viene habilitado.
Components src world kernel
src indica que se deben actualizar tanto las fuentes del kernel como las de las core userlandtools.
Identificación de hardware:
Lo primero que debemos hacer es saber de que hardware disponemos, esto podemos averiguarlo mediante los siguientes fichero/comandos:
/var/run/dmesg.boot
dmidecode
lspci -lv
kldstat
En mi caso se trata de un portátil DELL bastante viejo pero que sigue dando servicio:
Del XPS Studio XPS 1340 - A15
Sistema BIOS
A continuación pongo el hardware identificado vía /var/run/dmesg.boot, no pongo todo el hardware detectado, tan solo el que voy a utilizar, por ejemplo las tarjetas PCM-CIA, el lector de tarjetas SD y la tarjeta de sonido no les doy uso así que no añadiré soporte en mi kernel:
CPU: Intel(R) Core(TM)2 Duo CPU P9500 @ 2.53GHz (2533.32-MHz K8-class CPU)
ahci0: <NVIDIA MCP79 AHCI SATA controller> port 0x30e8-0x30ef,0x30dc-0x30df,0x30e0-0x30e7,0x30d8-0x30db,0x30c0-0x30cf mem 0xf0884000-0xf0885fff irq 19 at device 11.0 on pci0
ohci0: <nVidia nForce MCP79 USB Controller> mem 0xf0886000-0xf0886fff irq 20 at device 4.0 on pci0
ehci0: <NVIDIA nForce MCP79 USB 2.0 controller> mem 0xf0889000-0xf08890ff irq 21 at device 4.1 on pci0
ohci1: <nVidia nForce MCP79 USB Controller> mem 0xf0887000-0xf0887fff irq 22 at device 6.0 on pci0
nfe0: <NVIDIA nForce MCP79 Networking Adapter> port 0x30d0-0x30d7 mem 0xf0888000-0xf0888fff,0xf0889c00-0xf0889cff,0xf0889800-0xf088980f irq 18 at device 10.0 on pci0
miibus0: <MII bus> on nfe0
atkbd0: <AT Keyboard> irq 1 on atkbdc0
ada0: <Samsung SSD 870 EVO 1TB SVT02B6Q> ACS-4 ATA SATA 3.x device
cd0: <HL-DT-ST DVD+-RW GS20N A106> Removable CD-ROM SCSI device
da0: <WD 10EZEX External 1.75> Fixed Direct Access SPC-2 SCSI device
ath0: <Atheros 9280> mem 0xf0400000-0xf040ffff irq 22 at device 0.0 on pci5
Compilación del kernel:
Primero comprobamos que imagen de kernel tenemos cargada actualmente:
FreeBSD MightyMax.alfaexploit.com 13.1-RELEASE-p2 FreeBSD 13.1-RELEASE-p2 GENERIC amd64
Si necesitamos consultar algún parámetro del kernel GENERIC podemos hacerlo en el siguiente fichero:
/usr/src/sys/$(uname -m)/conf/GENERIC
Deshabilitar opciones del kernel tan solo implica que no se compilará dicha opción dentro del kernel, por defecto siempre se compilan TODOS los módulos incluso de opciones incluidas dentro del kernel, para evitar compilar módulos innecesarios FreeBSD soporta varios parámetros de compilación interesantes:
MODULES_OVERRIDE
(str) Set to a list of modules to build instead of all of
them.
WITHOUT_MODULES
(str) Set to a list of modules to exclude from the build.
This provides a somewhat easier way to exclude modules you
are certain you will never need than specifying
MODULES_OVERRIDE. This is applied after MODULES_OVERRIDE.
De este modo el tiempo de compilación se verá reducido drásticamente.
Como punto de partida vamos a utilizar la configuración MINIMAL que deja dentro del kernel el mínimo de opciones y externaliza como módulo el máximo posible:
/usr/src/sys/$(uname -m)/conf/MINIMAL
Para tener un kernel lo mas limpio posible tendremos que seguir los siguientes pasos:
- Copiar MINIMAL y eliminar de la config todo lo que no necesitemos.
- Compilar un kernel MINIMAL.
- Mirar los módulos compilados para añadir dentro de nuestro kernel los del hardware mas utilizado.
- Compilar nuestro kernel con la opción MODULES_OVERRIDE indicando los módulos del hardware menos utilizado.
Con esto obtenemos un kernel con soporte para el hardware mas utilizado dentro del kernel y algunos módulos del hardware menos utilizado.
Una de las tareas mas complicadas cuando compilamos el kernel es saber que funcionalidad corresponde con cada opción, para averiguarlo tenemos varias opciones:
Grepear el nombre en las configs:
/usr/src/sys/amd64/conf/GENERIC:device aac # Adaptec FSA RAID
Consultar las notas de la arquitectura en concreto:
/usr/src/sys/conf/NOTES
/usr/src/sys/amd64/conf/NOTES
Consultar la información online del handbook, las páginas de man o la web de GhostBSD:
https://docs.freebsd.org/es/books/handbook/kernelconfig/
https://www.freebsd.org/cgi/man.cgi?manpath=FreeBSD+13.1-RELEASE+and+Ports
https://wiki.ghostbsd.org/index.php/Kernel
Googlear:
X freebsd module
Una buena forma de detectar dependencias es grepeando el código fuente del kernel, por ejemplo:
MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION);
MODULE_DEPEND(smbfs, libiconv, 1, 1, 2);
MODULE_DEPEND(smbfs, libmchain, 1, 1, 1);
Según sea una funcionalidad muy utilizada o poco utilizada la añadiremos a la imagen del kernel o la añadiremos a la variable de compilación MODULES_OVERRIDE.
Algunos módulos no se pueden compilar de forma específica si no que forman parte de un paquete, por ejemplo los algoritmos de control de congestión TCP o se compilan todos o ninguno, como podemos ver solo existe un módulo llamado cc no cc_cdg, cc_chd, cc_cubic… el módulo cc los incluye todos:
total 33
drwxr-xr-x 9 root wheel 10 Aug 12 04:48 .
drwxr-xr-x 442 root wheel 444 Aug 12 04:48 ..
-rw-r--r-- 1 root wheel 432 Aug 12 04:48 Makefile
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_cdg
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_chd
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_cubic
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_dctcp
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_hd
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_htcp
drwxr-xr-x 2 root wheel 3 Aug 12 04:48 cc_vegas
NOTA: Con algunos otros módulos como geom pasa igual.
Mientras tengamos corriendo el kernel GENERIC podremos consultar que opciones fueron incluidas en él:
device aac
O las opciones compiladas como módulo:
-r-xr-xr-x 1 root wheel 123224 May 23 20:16 /boot/kernel/aac.ko
Procedemos con la compilación del kernel MINIMAL para que nos genere una imagen reducida y un buen puñado de módulos.
time make -j3 buildkernel KERNCONF=MINIMAL INSTKERNNAME=kernel.minimal
Lo instalamos:
Ahora podemos ver la configuración minimal en el fichero MINIMAL y los módulos en /boot/kernel.minimal/*.ko, ahora tan solo debemos deshabilitar todo lo innecesario de la config MINIMAL y decidir que módulos meteremos dentro de la imagen, que módulos fuera y que módulos eliminar.
Antes de tocar nada hacemos una copia de la configuración genérica:
mkdir /root/kernel_configs
cp GENERIC /root/kernel_configs/
Y copiamos la MINIMAL a una config propia que llamaremos KR0M-MINIMAL:
ln -s /root/kernel_configs/KR0M-MINIMAL KR0M-MINIMAL
lrwxr-xr-x 1 root wheel 33 Aug 29 01:13 KR0M-MINIMAL -> /root/kernel_configs/KR0M-MINIMAL
Debemos tener en cuenta que el fichero DEFAULTS se incluye automáticamente cuando se ejecuta el comando buildkernel, no hay que editar el fichero, si no deshabilitar las opciones que no queramos en nuestra custom config, por ejemplo yo no necesito UART(puerto serie).
Editamos nuestra configuración dejándola del siguiente modo:
cpu HAMMER
ident KR0M-MINIMAL
nodevice uart_ns8250 # No UART needed
device acpi
device atkbdc # AT keyboard controller
device atkbd # AT keyboard
device vt # Virtual terminal console driver
device vt_vga # Virtual terminal console driver VGA mode
device vt_vbefb # VBE framebuffer
device loop # Network loopback
device ether # Ethernet support
device bpf # Berkeley packet filter
device if_bridge # Bridge interfaces
device acpi_wmi # Interface for vendor specific WMI implementations
device ahci # Serial ATA Advanced Host Controller Interface driver
device pci # PCI/PCIe bus driver: ahci dep
device ata # Generic ATA/SATA controller driver: MCP79
device scbus # Common Access Method Storage subsystem: ata/ahci dep
device ada # ATA Direct Access device driver
device da # SCSI Direct Access device driver
device cd # SCSI CD-ROM driver
device usb # Universal Serial Bus
device ehci # USB Enhanced Host Controller
device ohci # OHCI USB Host Controller driver
device ctl # CAM Target Layer
device cfumass # USB Mass Storage Class Transport
device coretemp # Intel Core on-die digital thermal sensor
device cpuctl # To retrieve CPUID information and perform CPU firmware updates.
device cpufreq # CPU frequency control framework
device crypto # User-mode access to hardware-accelerated cryptography
device cryptodev # User-mode access to hardware-accelerated cryptography
device firmware # Interface for loading firmware images into the kernel
device miibus # Media Independent Interface network bus: nForce MCP Ethernet dep
device nfe # NVIDIA nForce MCP Ethernet driver
device iflib # Network Interface Driver Framework
device mem # Interface to the physical memory of the computer
device umass # USB Mass Storage Devices driver
device pass # CAM application passthrough driver
device ath # Wireless network interface: Atheros IEEE 802.11 wireless network driver
device ath_pci
device ath_hal
device ath_rate_sample
device wlan
device wlan_xauth # External authenticator support for 802.11 devices
device wlan_tkip # TKIP and Michael crypto support for 802.11 devices
device wlan_wep # WEP crypto support for 802.11 devices
device wlan_ccmp # AES-CCMP crypto support for 802.11 devices
device if_bridge # network bridge device
device epair # A pair of virtual back-to-back connected Ethernet interfaces
options VIMAGE # VNET/Vimage support
options RACCT # Resource accounting framework
options RCTL # Resource limits
options SCHED_ULE # ULE scheduler
options PREEMPTION # Enable kernel thread preemption
options SMP # Symmetric MultiProcessor Kernel
options INET # InterNETworking
options INET6 # IPv6 communications protocols: Iocage/Bastille dep
options TCP_OFFLOAD # TCP offload
options ZFS # Zettabyte File System
options MD_ROOT # MD is a potential root device
options COMPAT_FREEBSD11 # Compatible with FreeBSD11
options COMPAT_FREEBSD12 # Compatible with FreeBSD12
options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI
options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
options PRINTF_BUFR_SIZE=128 # Prevent printf output being interspersed.
options AUDIT # Security event auditing
options CAPABILITY_MODE # Capsicum capability mode
options CAPABILITIES # Capsicum capabilities
options INCLUDE_CONFIG_FILE # Include this file in kernel
options EARLY_AP_STARTUP # Start Application Processors earlier during boot
options IOMMU # input–output memory management unit
options VESA # Add support for VESA BIOS Extensions (VBE)
options MAC # TrustedBSD MAC Framework
options HZ=1000 # Set the timer granularity, recomended value by dummynet
options DEVICE_POLLING # Device Polling
options SYSVMSG # SYSV-style message queues
options SYSVSEM # SYSV-style semaphores
options SYSVSHM # SYSV-style shared memory
options TMPFS # Tmpfs file system
options ZSTDIO # zstd-compressed kernel and user dumps: ZFS dep
options NFSCL # Network Filesystem Client: ZFS dep
options NFSD # Network Filesystem Server: ZFS dep
options NFSLOCKD # Network Lock Manager: ZFS dep
options NFS_ROOT # NFS usable as /, requires NFSCL: ZFS dep
options KDTRACE_FRAME # Ensure frames are compiled in
options KDTRACE_HOOKS # Kernel DTrace hooks
options COMPAT_LINUX32 # Required for linux compat layer
options COMPAT_FREEBSD32 # Required for poudriere compilation
En cuanto a los módulos he decidido dejar estos:
-
accf_data : buffer incoming connections until data arrives.
-
accf_dns : buffer incoming DNS requests until the whole first request is present.
-
accf_http : buffer incoming connections until a certain complete HTTP requests arrive.
-
acl_nfs4: introduction to the POSIX.1e/NFSv4 ACL security API.
-
acl_posix1e: introduction to the POSIX.1e/NFSv4 ACL security API.
-
cc: TCP Congestion Control algorithms.
-
cd9660: ISO-9660 file system.
-
cd9660_iconv: ISO-9660 file system.
-
fdescfs: Provides access to the per-process file descriptor namespace in the global file system namespace.
-
fusefs: File system that is serviced by a userspace program.
-
mac_ntpd: Policy allowing ntpd to run as non-root user.
-
nullfs: The nullfs driver will permit the FreeBSD kernel to mount a loopback file system sub-tree.
-
opensolaris: ZFS requirement.
-
udf: Universal Disk Format.
-
udf_iconv: Universal Disk Format.
-
pf: packet filter
-
pflog: packet filter logging interface
-
pfsync: packet filter state table sychronisation interface
-
ipfw: IP packet filter and traffic accounting
-
ipfw_nat: ipfw NAT
-
ipfw_nat64: ipfw NAT IPv4 to IPv6
-
ipfw_nptv6: IPv6 network prefix translation facility
-
ipfw_pmod: packet modification facility
-
dtrace: DTrace dynamic tracing compiler and tracing utility
-
dtraceall: DTrace dynamic tracing compiler and tracing utility
-
linprocfs: Required to compile using poudriere
-
pseudofs: Required to compile using poudriere
-
linux_common: Required to compile using poudriere
-
procfs: Required to compile using poudriere
-
linux: Required to compile using poudriere
-
linux64: Required to compile using poudriere
NOTA: Si incluimos ipfw dentro del kernel este será cargado siempre de forma independiente de lo que se especifique en el fichero /etc/rc.conf, incluso con firewall_enable=“NO” seguirá cargándolo, la única manera de deshabilitarlo es mediante firewall_type=“open”.
Compilamos nuestro kernel personalizado:
time make -j3 buildkernel KERNCONF=KR0M-MINIMAL INSTKERNNAME=kernel.kr0m-minimal MODULES_OVERRIDE="accf_data accf_dns accf_http acl_nfs4 acl_posix1e cc cd9660 cd9660_iconv fdescfs fusefs mac_ntpd nullfs opensolaris udf udf_iconv pf pflog pfsync ipfw ipfw_nat ipfw_nat64 ipfw_nptv6 ipfw_pmod dtrace linprocfs pseudofs linux_common procfs linux linux64"
Lo instalamos:
Se ha definido la variable INSTKERNNAME para que no sobreescriba el kernel original, ya que luego es muy complicado mantener el sistema operativo actualizado con nuestro kernel custom corriendo y una versión actualizada de la imagen GENERIC como recomiendan aquí , de este modo freebsd-update actualizará /boot/kernel al margen de /boot/kernel.kr0m-minimal
Si fuese necesario podríamos afinar mas el proceso de compilación mediante el fichero /etc/make.conf
Arrancamos con el kernel nuevo:
shutdown -r now
Comprobamos que imagen tenemos cargada:
FreeBSD MightyMax.alfaexploit.com 13.1-RELEASE-p2 FreeBSD 13.1-RELEASE-p2 #19 releng/13.1-n250155-514a191356c-dirty: Thu Sep 1 13:01:03 CEST 2022 root@MightyMax.alfaexploit.com:/usr/obj/usr/src/amd64.amd64/sys/KR0M-MINIMAL amd64
Si funciona correctamente editamos la configuración del loader para que sea el kernel por defecto:
kernel=kernel.kr0m-minimal
NOTA: Los módulos del kernel normalmente se encuentran en /boot/KERNEL_NAME/*.ko a excepción de módulos de terceras partes como podría ser Nvidia o VirtualBox que se encuentran en /boot/modules/*.ko
El hardware donde se ha compilado el kernel es el siguiente:
hw.model: Intel(R) Core(TM)2 Duo CPU P9500 @ 2.53GHz
hw.machine: amd64
hw.ncpu: 2
Con 8Gb de RAM:
total used free shared buffers cached
Mem: 7664 1307 6356 0 0 0
Swap: 2048 0 2048
A continuación la diferencia entre compilar un kernel GENERIC y compilar uno mínimo:
CUSTOM:
real 4m56.748s
user 8m54.733s
sys 0m33.359s
GENERIC:
real 30m56.621s
user 54m24.386s
sys 4m48.999s
La diferencia es abismal, hemos pasado de tardar 30m aproximadamente a unos 5m
En cuanto al tamaño también apreciamos diferencia:
CUSTOM:
du -h /boot/kernel.kr0m-minimal/kernel
6.8M /boot/kernel.kr0m-minimal/kernel
du -sch /boot/kernel.kr0m-minimal/*.ko
209K total
GENERIC:
du -h /boot/kernel/kernel
17M /boot/kernel/kernel
du -sch /boot/kernel/*.ko
80M total
Troubleshooting:
Deshabilitar IPv6 puede dar problemas ya que muchos softwares como IOCage/Bastille asumen que estará disponible incluso sin utilizar IPv6 en las jails, así que no recomiendo deshabilitarlo.
Bastille tiene como requerimiento
PF
para natear y redirigir puertos, así que debemos compilarlo en el kernel o como módulo, yo me he decantado como módulo.
Hay ocasiones en las que una actualización no actualizará el kernel pero si otras partes del sistema, este nivel de parcheo del sistema se puede consultar mediante el comando:
13.0-RELEASE-p11
Para que nuestro custom kernel refleje correctamente el patchlevel del sistema debemos recompilarlo de nuevo, de este modo la imagen será generada con la información actualizada del patchlevel. Con el kernel GENERIC pasa lo mismo, si no cargamos la imagen nueva tampoco refleja correctamente el patchlevel.
Resumiendo
- En todas las actualizaciones debemos recompilar el kernel para que cuadre con la versión de las core userlandtools y las fuentes tanto de las core userlandtools como del kernel.
- Si además queremos reflejar correctamente el patchlevel de las core userlandtools también debemos reiniciar con el kernel recién compilado.
Si nuestro nuevo kernel tuviese problemas para arrancar siempre podremos cargar la imagen GENERIC o la imagen anterior desde el boot loader de FreeBSD, tan solo seleccionaríamos la opción “Escape to loader prompt” y teclear el nombre de la imagen.
Escape to loader prompt
boot kernel
boot kernel.kr0m.old
Puede darse el caso en el que el kernel arranque pero algunos comandos como ps, vmstat o conexiones de forma aparentemente random fallen, esto es debido a que las utilidades del sistema fueron compiladas para una versión del kernel muy distinta a la actual.
Para solventarlo debemos proceder de forma distinta dependiendo de si compilamos “world” y las core userland tools o utilizamos la versión binaria de estas:
- Source: Recompilar e instalar las core userlandtools utilizando una versión de las fuentes a la par con la versión del kernel.
- Binarios: Se debería actualizar mediante freebsd-update a una versión de FreeBSD que lleve los binarios compilados con la misma versión de kernel que estemos utilizando.
Podemos consultar la versión del kernel y las userlandtools del siguiente modo:
- freebsd-version -r -> Kernel que está corriendo actualmente.
- freebsd-version -k -> Kernel instalado pero que todavía no ha cargado porque no se ha reiniciado el equipo.
- freebsd-version -u -> Versión de las core userlandtools.
Si no disponemos de una imagen GENERIC por la razón que sea podemos regenerarla del siguiente modo:
make -j3 kernel __MAKE_CONF=/dev/null SRCCONF=/dev/null
Este proceso solo tiene como requisitos no haber tocado la configuración GENERIC ni compilar con parámetros retocados en /etc/make.conf
También podemos consultar los parámetros con los que fué compilado nuestro kernel si este fué compilado con la opción INCLUDE_CONFIG_FILE habilitada(en GENERIC lo está):
kern.conftxt: options CONFIG_AUTOGENERATED
ident KR0M-MINIMAL
machine amd64
cpu HAMMER
options NFS_ROOT
options NFSLOCKD
options NFSD
...
Dejo algunos enlaces de referencia que pueden resultar de ayuda:
https://docs.freebsd.org/en/books/handbook/kernelconfig/
https://docs.freebsd.org/en/books/handbook/mirrors/index.html#git