Setting up a FreeBSD package mirror on TrueNAS Core
To keep TrueNAS Core running with security fixes, we need to be able to apply patches and rebuild some components from time to time. FreeBSD’s native build tools are already present on Core 13.3, but some userspace components, including ZFS’s, require build tools installed from packages. Public mirrors are dropping unsupported FreeBSD releases, so let’s host our own in the LAN.
This opus describes how to do precisely that. My 13.3 collection now contains 34,346 packages and takes about 120 GiB.
Saving the catalog
Set upstream to the collection directory’s full URL, including the protocol, hostname, and path. The URL below is the only one I found that still serves both the FreeBSD 13 release_4 collection and its bootstrap signature1. archive is the local directory.
On the TrueNAS host, run (replace tank with your pool name; we’ll create the package-mirror dataset there):
sudo sh
midclt call pool.dataset.create '{"name":"tank/package-mirror"}'
archive=/mnt/tank/package-mirror/13.3
upstream='https://mirror.yandex.ru/mirrors/freebsd-pkg/FreeBSD:13:amd64/release_4'
mkdir -p "$archive/control/repos" "$archive/control/db" "$archive/control/cache" "$archive/repository"
for file in meta.conf data.pkg packagesite.pkg; do
fetch -o "$archive/repository/$file" "$upstream/$file"
done
Configure a separate database, cache, and repository for pkg, then import the catalog:
cat > "$archive/control/pkg.conf" <<EOF
PKG_DBDIR: "$archive/control/db"
PKG_CACHEDIR: "$archive/control/cache"
REPOS_DIR: ["$archive/control/repos"]
REPO_AUTOUPDATE: false
EOF
cat > "$archive/control/repos/Archive.conf" <<EOF
Archive: {
url: "$upstream",
mirror_type: "none",
signature_type: "fingerprints",
fingerprints: "/usr/share/keys/pkg"
}
EOF
pkg -C "$archive/control/pkg.conf" update
-C targets this configuration, keeping the host’s package database untouched.2 Automatic catalog updates are disabled so the package list stays fixed during the download.
Downloading the packages
Download the complete collection:
pkg -C "$archive/control/pkg.conf" fetch -a -o "$archive/repository"
Rerun after an interrupted download; valid files are reused.
Add the package manager’s bootstrap link and signature:
mkdir -p "$archive/repository/Latest"
ln -s ../All/pkg-1.21.3.pkg "$archive/repository/Latest/pkg.pkg"
fetch -o "$archive/repository/Latest/pkg.pkg.sig" "$upstream/Latest/pkg.pkg.sig"
Saving the release files
If 13.3 is not cached, fetch it from FreeBSD’s release archive:
iocage fetch -r 13.3-RELEASE -s https://archive.freebsd.org -d old-releases/amd64/amd64 --noupdate --noeol
That archive may disappear too. Save the release files alongside the packages, using FreeBSD’s directory layout:3
mkdir -p "$archive/repository/releases/amd64/amd64/13.3-RELEASE"
cd /mnt/tank/iocage/download/13.3-RELEASE
cp MANIFEST base.txz lib32.txz src.txz "$archive/repository/releases/amd64/amd64/13.3-RELEASE/"
After restoring the dataset, import the release:
iocage activate tank
iocage fetch -f -d /mnt/tank/package-mirror/13.3/repository/releases/amd64/amd64 -r 13.3-RELEASE --noupdate --noeol
Serving the mirror
Create the server jail, mount the repository read-only, and enter it:
iocage create -n package-mirror -r 13.3-RELEASE dhcp=1 vnet=1 bpf=1 boot=1
mkdir -p /mnt/tank/iocage/jails/package-mirror/root/mnt/repository
iocage fstab -a package-mirror "$archive/repository" /mnt/repository nullfs ro 0 0
iocage console -f package-mirror
Inside the jail, configure pkg to use the mounted repository and install Nginx:
exec sh
mkdir -p /usr/local/etc/pkg/repos
cat > /usr/local/etc/pkg/repos/FreeBSD.conf <<'EOF'
FreeBSD: {
url: "file:///mnt/repository",
mirror_type: "none",
signature_type: "fingerprints",
fingerprints: "/usr/share/keys/pkg"
}
EOF
pkg install nginx
Configure Nginx to serve /13.3/, start it, and exit the jail:
cat > /usr/local/etc/nginx/nginx.conf <<'EOF'
events {}
http {
server {
listen 80;
location /13.3/ {
alias /mnt/repository/;
}
}
}
EOF
sysrc nginx_enable=YES
service nginx start
exit
Back on the host, get the jail’s address:
iocage exec package-mirror ifconfig epair0b inet
Using the mirror
Create and enter client jail:
iocage create -n new-jail -r 13.3-RELEASE dhcp=1 vnet=1 bpf=1
iocage console -f new-jail
Inside new-jail, replace <mirror-ip> in the HTTP URL1 with the repository jail’s LAN IP obtained above:
exec sh
mkdir -p /usr/local/etc/pkg/repos
cat > /usr/local/etc/pkg/repos/FreeBSD.conf <<'EOF'
FreeBSD: {
url: "http://<mirror-ip>/13.3",
mirror_type: "none",
signature_type: "fingerprints",
fingerprints: "/usr/share/keys/pkg"
}
EOF
Now pkg can be used to install packages normally:
pkg install git vim gmake
Footnotes
-
signature_type: "fingerprints"checks catalog and bootstrap signatures against the trusted fingerprints shipped with FreeBSD under/usr/share/keys/pkg. Packages are checked against hashes in the signed catalog. HTTPS isn’t required for this LAN mirror. ↩︎ ↩︎ -
pkg updatedownloads the catalog again, imports it into its database, and discards the downloaded archives. The copies fetched above retain the signed catalog for clients of our mirror. ↩︎ -
The two
amd64directories identify the platform and processor architecture. Both areamd64here; see FreeBSD’s directory layout. ↩︎