<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Freebsd on inetshell</title><link>https://inet.sh/en/tags/freebsd/</link><description>Recent content in Freebsd on inetshell</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 25 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/en/tags/freebsd/index.xml" rel="self" type="application/rss+xml"/><item><title>FreeBSD: replace a disk in the ZFS boot mirror (zroot) with gpart</title><link>https://inet.sh/en/posts/zfs/freebsd-replace-boot-mirror-disk/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/zfs/freebsd-replace-boot-mirror-disk/</guid><description>Swap a dead disk out of a zroot mirror on FreeBSD 13/14: clone the partition table with gpart backup | gpart restore, reinstall the BIOS bootcode (pmbr + gptzfsboot) and populate the ESP with loader.efi for UEFI (boot1.efi is deprecated), zpool replace or attach with the right partition, swap, and resilver.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>Good disk <code>ada0</code>, new disk <code>ada3</code>, the dead one was <code>ada1</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool status zroot                                  <span class="c1"># ada1p4 FAULTED / UNAVAIL</span>
</span></span><span class="line"><span class="cl">gpart backup ada0 <span class="p">|</span> gpart restore -F ada3           <span class="c1"># clone the partition table</span>
</span></span><span class="line"><span class="cl">gpart show ada3                                     <span class="c1"># p1 efi, p2 freebsd-boot, p3 freebsd-swap, p4 freebsd-zfs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># BIOS bootcode: the index is the freebsd-boot partition&#39;s</span>
</span></span><span class="line"><span class="cl">gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i <span class="m">2</span> ada3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># UEFI: populate the ESP</span>
</span></span><span class="line"><span class="cl">newfs_msdos -F <span class="m">32</span> -c <span class="m">1</span> /dev/ada3p1
</span></span><span class="line"><span class="cl">mount -t msdosfs /dev/ada3p1 /mnt
</span></span><span class="line"><span class="cl">mkdir -p /mnt/EFI/BOOT /mnt/EFI/FREEBSD
</span></span><span class="line"><span class="cl">cp /boot/loader.efi /mnt/EFI/BOOT/BOOTX64.EFI
</span></span><span class="line"><span class="cl">cp /boot/loader.efi /mnt/EFI/FREEBSD/LOADER.EFI
</span></span><span class="line"><span class="cl">umount /mnt
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># The new disk joins the mirror in the dead one&#39;s place</span>
</span></span><span class="line"><span class="cl">zpool replace zroot ada1p4 ada3p4
</span></span><span class="line"><span class="cl">zpool status zroot                                  <span class="c1"># resilver in progress</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>When <code>zpool status</code> shows <code>ONLINE</code> on both, test booting with <strong>only</strong> the new disk.</p>
<h2 id="before-starting">Before starting</h2>
<ul>
<li><strong>Identify the disk.</strong> <code>zpool status</code> names the dead member (<code>ada1p4</code>, or a long GUID if it is already gone). <code>camcontrol devlist</code> lists the disks with model and serial so you know which bay to touch.</li>
<li><strong>If the bad disk is still connected</strong>, take it out of the pool before pulling the cable: <code>zpool offline zroot ada1p4</code>.</li>
<li><strong>Layout.</strong> A default FreeBSD 13/14 zroot install creates, per disk: <code>p1</code> <code>efi</code>, <code>p2</code> <code>freebsd-boot</code>, <code>p3</code> <code>freebsd-swap</code>, <code>p4</code> <code>freebsd-zfs</code>. Old or hand-made installs vary: look at <code>gpart show ada0</code> and adjust the indexes. The bootcode&rsquo;s <code>-i</code> is <code>freebsd-boot</code>&rsquo;s.</li>
<li><strong>The new disk must be the same size or bigger.</strong> <code>gpart restore</code> copies exact sizes; if it is bigger, the rest stays free and you can use it later.</li>
</ul>
<h2 id="why-partitions-and-not-the-whole-disk">Why partitions and not the whole disk</h2>
<p>In a boot pool every disk carries the boot partitions in addition to the ZFS one. If you run <code>zpool replace zroot ada1p4 ada3</code> with the raw disk, the mirror works but that disk <strong>cannot boot</strong>: the day <code>ada0</code> dies, you will have the data and no loader. That is why the order is: partition identically, bootcode, and only then <code>zpool replace</code> with the partition.</p>
<h2 id="step-by-step">Step by step</h2>
<p><strong>1. Clone the partition table.</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpart backup ada0 <span class="p">|</span> gpart restore -F ada3
</span></span><span class="line"><span class="cl">gpart show ada3
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-F</code> destroys whatever was on <code>ada3</code>. If the pool uses GPT labels (<code>gpt/zfs0</code> in <code>zpool status</code>) instead of <code>adaXpY</code> names, give the new one its own: <code>gpart modify -i 4 -l zfs1 ada3</code>, and use <code>gpt/zfs1</code> in the <code>zpool replace</code>.</p>
<p><strong>2. Bootcode.</strong> Install both: that way it boots whether the board is in BIOS/CSM mode or UEFI.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># BIOS: protective MBR + gptzfsboot on the freebsd-boot partition (p2 in the default layout)</span>
</span></span><span class="line"><span class="cl">gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i <span class="m">2</span> ada3
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># UEFI: the ESP (p1) is a FAT32 with loader.efi at the standard paths</span>
</span></span><span class="line"><span class="cl">newfs_msdos -F <span class="m">32</span> -c <span class="m">1</span> /dev/ada3p1
</span></span><span class="line"><span class="cl">mount -t msdosfs /dev/ada3p1 /mnt
</span></span><span class="line"><span class="cl">mkdir -p /mnt/EFI/BOOT /mnt/EFI/FREEBSD
</span></span><span class="line"><span class="cl">cp /boot/loader.efi /mnt/EFI/BOOT/BOOTX64.EFI
</span></span><span class="line"><span class="cl">cp /boot/loader.efi /mnt/EFI/FREEBSD/LOADER.EFI
</span></span><span class="line"><span class="cl">umount /mnt
</span></span></code></pre></td></tr></table>
</div>
</div><p>Old guides say <code>gpart bootcode -p /boot/boot1.efifat -i 1</code>; <code>boot1.efi</code> is deprecated on current FreeBSD and the path documented in <code>loader.efi(8)</code> is copying <code>loader.efi</code> onto the ESP. If you want a dedicated firmware entry: <code>efibootmgr -c -l /mnt/EFI/FREEBSD/LOADER.EFI -L FreeBSD-ada3 -a</code>.</p>
<p><strong>3. Replace in the pool.</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool replace zroot ada1p4 ada3p4
</span></span></code></pre></td></tr></table>
</div>
</div><p>If the dead member no longer shows and the mirror is down to one disk, instead of <code>replace</code> you <strong>attach</strong> to the survivor: <code>zpool attach zroot ada0p4 ada3p4</code>.</p>
<p><strong>4. Resilver.</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">zpool status zroot           <span class="c1"># scan: resilver in progress ... % done</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Hours, depending on how full the pool is. Optional, so the resilver yields less to normal load:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sysctl vfs.zfs.resilver_min_time_ms<span class="o">=</span><span class="m">5000</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>5. Swap.</strong> If <code>/etc/fstab</code> had <code>/dev/ada1p3</code> as swap, change it to <code>/dev/ada3p3</code> (or better, to <code>gpt/swapN</code> labels that do not change with the disk name) and <code>swapon -a</code>.</p>
<p><strong>6. Check the boot.</strong> With the resilver done, power off, disconnect <code>ada0</code> (or change the boot order) and boot from the new one. If it comes up, reconnect and <code>zpool status</code> must still read <code>ONLINE</code> with no complaints. A closing <code>zpool scrub zroot</code> does not hurt.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong><code>gpart restore -F</code> does not ask.</strong> Double-check which disk is the new one; <code>camcontrol devlist</code> and <code>diskinfo -v ada3</code> (serial) before writing.</li>
<li><strong>Bootcode of a single kind</strong> leaves the disk bootable in only one firmware mode. Install both.</li>
<li><strong>Different physical sector size</strong> (512 vs 4K) can make <code>zpool replace</code> complain about <code>ashift</code>; in a modern mirror with <code>ashift=12</code> a 4Kn disk goes in fine, one with <code>ashift=9</code> does not.</li>
<li><strong>Bigger disk</strong>: after the resilver, <code>gpart resize -i 4 ada3</code> and <code>zpool online -e zroot ada3p4</code>; once both mirror disks are big, the pool grows.</li>
<li><strong><code>adaX</code> names shift</strong> when disks are removed; GPT labels and GUIDs do not. For the operation use whatever <code>zpool status</code> shows at that moment.</li>
</ul>
<h2 id="see-also">See also</h2>
<ul>
<li><a href="/en/posts/zfs/replace-disks/">Replace a disk in a ZFS pool</a> — the Linux/Proxmox version, without boot partitions.</li>
<li><a href="/en/posts/zfs/import-after-power-outage/">Import a pool after a power outage</a>.</li>
<li>FreeBSD: <code>man 8 loader.efi</code>, <code>man 8 gpart</code>, and <a href="https://farrokhi.net/posts/2020/05/replacing-a-faulty-disk-in-zfs/">Babak Farrokhi&rsquo;s article</a> on the BIOS replacement.</li>
</ul>
]]></content:encoded></item><item><title>pfctl: table cheat sheet for pfSense, OPNsense and FreeBSD</title><link>https://inet.sh/en/posts/pfsense/pfctl-tables-cheat-sheet/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/pfsense/pfctl-tables-cheat-sheet/</guid><description>pfctl -t/-T commands for PF tables: list, show, add, delete, test, flush, reload from a file and expire entries. With pfSense&amp;#39;s internal tables (sshguard, virusprot, snort2c, bogons, aliases and pfBlockerNG) and what gets lost on a filter reload.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pfctl -sT                                    <span class="c1"># loaded tables</span>
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T show                     <span class="c1"># contents</span>
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T add 192.168.1.10         <span class="c1"># add an IP or network (10.20.0.0/16 works too)</span>
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T delete 192.168.1.10
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T <span class="nb">test</span> 192.168.1.10        <span class="c1"># is it inside? (match / no match)</span>
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T flush                    <span class="c1"># empty the table</span>
</span></span><span class="line"><span class="cl">pfctl -t &lt;table&gt; -T replace -f /path/list    <span class="c1"># replace the whole contents from a file</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">pfctl -t sshguard -T flush                   <span class="c1"># pfSense: clear the failed-login lockout</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="what-a-table-is">What a table is</h2>
<p>A PF <strong>table</strong> is a set of addresses and networks that rules reference as <code>&lt;name&gt;</code>. It changes live, without reloading the ruleset, and holds thousands of entries with fast lookups; that is why PF uses tables for block lists, bogons, and anything that changes more often than the rules.</p>
<p>In <code>pf.conf</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">table &lt;blocklist&gt; persist file &#34;/etc/pf/blocklist.txt&#34;
</span></span><span class="line"><span class="cl">block in quick from &lt;blocklist&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>persist</code> keeps the table even when no rule uses it at the moment; <code>file</code> loads it from a file (one IP or network per line).</p>
<h2 id="commands">Commands</h2>
<p>All in the form <code>pfctl -t &lt;table&gt; -T &lt;command&gt;</code>:</p>
<table>
  <thead>
      <tr>
          <th>Command</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>show</code></td>
          <td>Lists the entries. With <code>-v</code>, per-entry counters (packets/bytes in/out)</td>
      </tr>
      <tr>
          <td><code>add &lt;ip|net&gt; …</code></td>
          <td>Adds one or more entries</td>
      </tr>
      <tr>
          <td><code>delete &lt;ip|net&gt; …</code></td>
          <td>Removes entries</td>
      </tr>
      <tr>
          <td><code>test &lt;ip&gt; …</code></td>
          <td>Says whether the address falls inside the table (without touching it)</td>
      </tr>
      <tr>
          <td><code>flush</code></td>
          <td>Empties the table; the table keeps existing</td>
      </tr>
      <tr>
          <td><code>kill</code></td>
          <td>Deletes the whole table (if no rule uses it)</td>
      </tr>
      <tr>
          <td><code>replace -f &lt;file&gt;</code></td>
          <td>Swaps the entire contents for the file&rsquo;s, in a single operation</td>
      </tr>
      <tr>
          <td><code>expire &lt;seconds&gt;</code></td>
          <td>Deletes entries whose counters have been idle for more than N seconds</td>
      </tr>
      <tr>
          <td><code>zero</code></td>
          <td>Zeroes the table&rsquo;s counters (or one entry&rsquo;s, if you name it)</td>
      </tr>
  </tbody>
</table>
<p>And without <code>-t</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pfctl -sT                    <span class="c1"># names of all tables</span>
</span></span><span class="line"><span class="cl">pfctl -vvsT                  <span class="c1"># global info per table: entries, evaluations, references</span>
</span></span><span class="line"><span class="cl">pfctl -T load -f /etc/pf.conf   <span class="c1"># reload only the table definitions from pf.conf</span>
</span></span><span class="line"><span class="cl">pfctl -sr <span class="p">|</span> grep <span class="s1">&#39;&lt;name&gt;&#39;</span>    <span class="c1"># which rules use a table</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="tables-on-pfsense">Tables on pfSense</h2>
<ul>
<li><strong>Aliases are tables.</strong> A Host, Network, or URL Table alias called <code>Admins</code> exists in PF as <code>&lt;Admins&gt;</code>; <code>pfctl -t Admins -T show</code> shows what pfSense resolved, including the FQDNs it already turned into IPs.</li>
<li><strong><code>sshguard</code></strong>: where IPs blocked for failed SSH or webGUI logins land. If you locked yourself out, <code>pfctl -t sshguard -T flush</code> (documented by Netgate under <a href="https://docs.netgate.com/pfsense/en/latest/troubleshooting/locked-out.html">Locked out of the WebGUI</a>).</li>
<li><strong><code>virusprot</code></strong>: hosts blocked for exceeding a rule&rsquo;s <em>Max. src. conn. rate</em> or <em>Max. states</em> limits. <code>pfctl -t virusprot -T show</code> and <code>-T delete &lt;ip&gt;</code> to release one.</li>
<li><strong><code>snort2c</code></strong>: Snort/Suricata blocks in legacy mode. <code>-T flush</code> to unblock everything.</li>
<li><strong><code>bogons</code> / <code>bogonsv6</code></strong>: the lists that update themselves when you enable <em>Block bogon networks</em> on an interface.</li>
<li><strong>pfBlockerNG</strong>: every list is a <code>pfB_*</code> alias. To know whether an IP is blocked by one: <code>pfctl -t pfB_Top_v4 -T test 203.0.113.7</code>.</li>
</ul>
<p>On OPNsense the mechanics are the same (<code>pfctl -sT</code> to see the names); the login lockout table is called <code>sshlockout</code>.</p>
<h2 id="what-gets-lost-on-reload">What gets lost on reload</h2>
<p>Any <code>-T add</code>, <code>-T delete</code>, or <code>-T flush</code> lives until the next <strong>filter reload</strong>: saving almost anything in the GUI, <code>Filter Reload</code> under Status → Filter Reload, or the alias update cron. At that point pfSense regenerates every table from its alias and your manual changes are gone. To make something permanent, edit the alias; for quick tests and unblocking, <code>pfctl</code> is perfect.</p>
<h2 id="beyond-tables">Beyond tables</h2>
<p>What gets used most alongside the above:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pfctl -sr                    <span class="c1"># loaded rules (with -vv, counters and rule numbers)</span>
</span></span><span class="line"><span class="cl">pfctl -ss                    <span class="c1"># state table</span>
</span></span><span class="line"><span class="cl">pfctl -si                    <span class="c1"># global statistics</span>
</span></span><span class="line"><span class="cl">pfctl -k 192.168.1.10        <span class="c1"># kill a host&#39;s states</span>
</span></span><span class="line"><span class="cl">pfctl -F states              <span class="c1"># kill all states (cuts every connection)</span>
</span></span><span class="line"><span class="cl">pfctl -d / pfctl -e          <span class="c1"># disable / enable PF (no PF means no NAT: only to get out of a lockout)</span>
</span></span><span class="line"><span class="cl">pfctl -nf /etc/pf.conf       <span class="c1"># syntax check without loading</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Full reference: the OpenBSD Handbook&rsquo;s <a href="https://www.openbsdhandbook.com/pf/cheat_sheet/">pfctl cheat sheet</a> and <code>man pfctl</code> on FreeBSD.</p>
]]></content:encoded></item><item><title>pfSense: ld-elf.so.1: Shared object "libssl.so.30" not found, required by "pkg"</title><link>https://inet.sh/en/posts/pfsense/libssl-so-30-not-found-pkg/</link><pubDate>Fri, 25 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/en/posts/pfsense/libssl-so-30-not-found-pkg/</guid><description>pkg update and the pfSense Package Manager die with &amp;#39;Shared object libssl.so.30 not found, required by pkg&amp;#39;: the pkg binary got linked against an OpenSSL the base system does not have yet. Recovery with pkg-static (certctl rehash, reinstall pkg, pfSense-repo and pfSense-upgrade) and how to finish the upgrade properly.</description><content:encoded><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>From the console (option 8) or <strong>Diagnostics → Command Prompt</strong>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">certctl rehash
</span></span><span class="line"><span class="cl">pkg-static clean -ay
</span></span><span class="line"><span class="cl">pkg-static install -fy pkg
</span></span><span class="line"><span class="cl">pkg-static install -xfy pfSense-repo pfSense-upgrade
</span></span><span class="line"><span class="cl">pkg-static update -f
</span></span><span class="line"><span class="cl">pfSense-upgrade -d -c        <span class="c1"># shows which version the configured branch offers</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>With <code>pkg</code> alive again, finish the pending upgrade through <strong>System → Update</strong> (or <code>pfSense-upgrade -d</code>), not with <code>pkg upgrade</code>.</p>
<h2 id="the-error">The error</h2>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">ld-elf.so.1: Shared object &#34;libssl.so.30&#34; not found, required by &#34;pkg&#34;
</span></span></code></pre></td></tr></table>
</div>
</div><p>You see it on <code>pkg update</code>, when installing a package, or indirectly: <strong>System → Package Manager</strong> shows up empty and <strong>System → Update</strong> cannot check versions. Sometimes the binary complaining is <code>pfSense-repoc</code> instead of <code>pkg</code>; it is the same problem.</p>
<h2 id="why-it-happens">Why it happens</h2>
<p>pfSense is FreeBSD, and <code>pkg</code> is a binary dynamically linked against the system&rsquo;s <code>libssl</code>. When the configured repository belongs to a pfSense release built on a newer FreeBSD, its packages come linked against a newer OpenSSL (<code>libssl.so.30</code> = OpenSSL 3). If <code>pkg</code>, or <code>pfSense-repoc</code>, gets updated from that repo <strong>before</strong> the base system, the new binary asks for a library that does not exist on your install yet.</p>
<p>How you get there:</p>
<ul>
<li>A manual <code>pkg upgrade</code> from the shell. On pfSense you never do that.</li>
<li>Installing or reinstalling a package after switching the <strong>Branch</strong> in System → Update to a higher version, without upgrading.</li>
<li>An upgrade interrupted halfway (typical in the 2.7.0 → 2.7.1/2.7.2 jump, when the base moved to OpenSSL 3).</li>
<li>On pfSense Plus, rolling back to an older boot environment with the repo pointing at the new one (Redmine #15070).</li>
</ul>
<p>The library name will change across releases; the mechanics and the fix stay the same.</p>
<h2 id="fix">Fix</h2>
<p><code>pkg-static</code> is the statically linked copy of <code>pkg</code>: it needs no <code>libssl</code>, which is why it works when <code>pkg</code> does not.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># 1. Let pkg-static talk TLS to the repo (holds until the next reboot)</span>
</span></span><span class="line"><span class="cl">certctl rehash
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. Drop the cache and reinstall pkg and the repo configuration</span>
</span></span><span class="line"><span class="cl">pkg-static clean -ay
</span></span><span class="line"><span class="cl">pkg-static install -fy pkg
</span></span><span class="line"><span class="cl">pkg-static install -xfy pfSense-repo pfSense-upgrade   <span class="c1"># -x: regex, so pfSense-repoc comes along</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. Refresh metadata and check</span>
</span></span><span class="line"><span class="cl">pkg-static update -f
</span></span><span class="line"><span class="cl">pfSense-upgrade -d -c
</span></span></code></pre></td></tr></table>
</div>
</div><p>If <code>pkg-static update</code> still fails, <code>pkg-static -d update</code> prints the full trace (certificates, DNS, wrong branch).</p>
<p>Before step 3 check the <strong>Branch</strong> under System → Update: it must be the version you want to reach, not an intermediate one. And finish the job through the GUI or with <code>pfSense-upgrade -d</code>: a stray <code>pkg upgrade</code> leaves you halfway again.</p>
<p>If the base system is so mixed up that even this does not fly, Netgate&rsquo;s documented last resort is <code>pkg-static upgrade -f</code> (reinstalls everything), followed by a reboot.</p>
<h2 id="traps">Traps</h2>
<ul>
<li><strong><code>pkg upgrade</code> does not exist on pfSense.</strong> The only supported path is System → Update / <code>pfSense-upgrade</code>. This whole situation is born from skipping it.</li>
<li><strong>Backup first</strong>: Diagnostics → Backup &amp; Restore. On ZFS, also a boot environment (System → Boot Environments) lets you roll back in one reboot.</li>
<li><strong>Do not copy <code>libssl.so.30</code> from another FreeBSD.</strong> It circulates as a trick; it leaves the system with libraries from two releases and breaks the next upgrade.</li>
<li><strong>Packages first, no.</strong> With an upgrade pending, upgrade the system and then install packages, not the other way around.</li>
<li>The repo and the upgrade use HTTPS: if <code>certctl rehash</code> is not enough, check the firewall&rsquo;s system time and DNS.</li>
</ul>
<p>Reference: <a href="https://docs.netgate.com/pfsense/en/latest/troubleshooting/upgrades.html">Upgrade Troubleshooting</a> in the Netgate documentation.</p>
]]></content:encoded></item></channel></rss>