<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"><channel><title>civilfritz.net</title><link>https://civilfritz.net/</link><description>Confusingly jumping between technology, philosophy, and gaming.</description><atom:link href="https://civilfritz.net/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2024 &lt;a href="mailto:janderson@civilfritz.net"&gt;Jonathon Anderson&lt;/a&gt; &lt;a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"&gt;&lt;img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /&gt;&lt;/a&gt;</copyright><lastBuildDate>Sun, 24 Mar 2024 06:48:48 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Splitting Warewulf Images Between PXE and NFS</title><link>https://civilfritz.net/anderbubble/splitting-warewulf-images-between-pxe-and-nfs/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;&lt;em&gt;&lt;a href="https://ciq.co/blog/splitting-warewulf-images-between-pxe-and-nfs/"&gt;This article was also published via the CIQ blog on 6 December 2022.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Warewulf 4 introduced compatibility with the OCI container ecosystem,
which greatly streamlines the process of defining, importing, and
maintaining compute node images compared to other systems--even
compared to Warewulf 3! But one aspect of compute node images remains
unchanged: they can quickly grow in size.&lt;/p&gt;
&lt;p&gt;Warewulf (and the technique of PXE-booting a node image more broadly)
expects that a compute node image will remain relatively small. Larger
sets of software, like you might provide via an Environment Modules
stack or, perhaps, via Spack, are typically deployed via a central NFS
share, which is then mounted at runtime by the booted compute
node. Even OpenHPC, with software packaged as operating system
containers, supports this paradigm, with packages installed on the
head node, landing in &lt;code&gt;/opt&lt;/code&gt;, and then being shared from the head node
to compute nodes.&lt;/p&gt;
&lt;p&gt;However, there are still benefits to maintaining this software as part
of a compute node image; but such a large image can quickly grow to
tens of gigabytes, making network booting difficult.&lt;/p&gt;
&lt;p&gt;In this article I'll demonstrate how a full software stack can be
managed together with a given compute node image, but the resultant
payload can be split in-place between PXE-served netbooting and an
NFS-mounted file system.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This procedure depends on support for &lt;code&gt;/etc/warewulf/excludes&lt;/code&gt;, which
was broken in Warewulf v4.3.0.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;The root image&lt;/h3&gt;
&lt;p&gt;First, I start with the standard Rocky Linux 8 image as published by
HPCng.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;container&lt;span class="w"&gt; &lt;/span&gt;import&lt;span class="w"&gt; &lt;/span&gt;docker://docker.io/warewulf/rocky:8&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Installing some software&lt;/h3&gt;
&lt;p&gt;Using the OpenHPC project as a source, I install a set of typical
scientific software. Most OpenHPC packages install software in &lt;code&gt;/opt&lt;/code&gt;
for distribution via NFS, which is what we're going to do: just a
little bit differently than usual.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;container&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split
&lt;span class="go"&gt;[rocky-8-split] Warewulf&amp;gt; dnf -y install 'dnf-command(config-manager)'&lt;/span&gt;
&lt;span class="go"&gt;[rocky-8-split] Warewulf&amp;gt; dnf config-manager --set-enabled powertools&lt;/span&gt;
&lt;span class="go"&gt;[rocky-8-split] Warewulf&amp;gt; dnf -y install epel-release http://repos.openhpc.community/OpenHPC/2/CentOS_8/x86_64/ohpc-release-2-1.el8.x86_64.rpm&lt;/span&gt;
&lt;span class="go"&gt;[rocky-8-split] Warewulf&amp;gt; dnf -y install valgrind-ohpc {netcdf,pnetcdf,hypre,boost}-gnu9-mpich-ohpc&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After installing the software our image is approaching 2GB. This isn't
egregious (and the compressed image as sent over the network is even
smaller), but gives us a point of comparison for what comes next.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;du&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;/var/lib/warewulf/container/rocky-8-split.img&lt;span class="o"&gt;{&lt;/span&gt;,.gz&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt;1.8G    /var/lib/warewulf/container/rocky-8-split.img&lt;/span&gt;
&lt;span class="go"&gt;651M    /var/lib/warewulf/container/rocky-8-split.img.gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Excluding the software from the final image&lt;/h3&gt;
&lt;p&gt;Warewulf consults &lt;code&gt;/etc/warewulf/excludes&lt;/code&gt; within the image itself to
define files that should not be included in the built image. For our
example here, I exclude the full contents of &lt;code&gt;/opt/&lt;/code&gt;, in anticipation
that we'll be mounting it via NFS in stead.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="go"&gt;[rocky-8-split] Warewulf&amp;gt; cat /etc/warewulf/excludes&lt;/span&gt;
&lt;span class="go"&gt;/boot&lt;/span&gt;
&lt;span class="go"&gt;/usr/share/GeoIP&lt;/span&gt;
&lt;span class="go"&gt;/opt/*&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Rebuilding the image with &lt;code&gt;/opt/*&lt;/code&gt; excluded, the image is reduced in
size, and further software installation would no longer increase the
final size of the image delivered over PXE.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;du&lt;span class="w"&gt; &lt;/span&gt;-h&lt;span class="w"&gt; &lt;/span&gt;/var/lib/warewulf/container/rocky-8-split.img&lt;span class="o"&gt;{&lt;/span&gt;,.gz&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="go"&gt;1.1G    /var/lib/warewulf/container/rocky-8-split.img&lt;/span&gt;
&lt;span class="go"&gt;483M    /var/lib/warewulf/container/rocky-8-split.img.gz&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Exporting the software via NFS&lt;/h3&gt;
&lt;p&gt;With the software in &lt;code&gt;/opt&lt;/code&gt; excluded from the image, we need to export
it via NFS in stead. This is relatively easily done, though we must
discover and hard-code paths to the container directory.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;readlink&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;container&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split&lt;span class="k"&gt;)&lt;/span&gt;/opt
&lt;span class="go"&gt;/var/lib/warewulf/chroots/rocky-8-split/rootfs/opt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add an NFS export to &lt;code&gt;/etc/warewulf/warewulf.conf&lt;/code&gt;, restart the
Warewulf server, and configure NFS with &lt;code&gt;wwctl&lt;/code&gt;. Note that I've
specified &lt;code&gt;mount: false&lt;/code&gt; for this export, as I want to control &lt;em&gt;which&lt;/em&gt;
nodes will mount it: presumably nodes that aren't using this image
should not mount this image's software.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nt"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;export paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/warewulf/chroots/rocky-8-split/rootfs/opt&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;export options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rw,sync,no_root_squash&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;restart&lt;span class="w"&gt; &lt;/span&gt;warewulfd
&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;configure&lt;span class="w"&gt; &lt;/span&gt;nfs
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Mounting the software on the compute node&lt;/h3&gt;
&lt;p&gt;We can mount this new NFS share just like any other, by listing it in &lt;code&gt;fstab&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Warewulf typically configures &lt;code&gt;fstab&lt;/code&gt; as part of the &lt;code&gt;wwinit&lt;/code&gt;
overlay. In order to mount this NFS share without setting &lt;code&gt;mount:
true&lt;/code&gt; for all nodes, I copy &lt;code&gt;fstab.ww&lt;/code&gt; to a new overlay and add an
additional entry.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split
&lt;span class="go"&gt;OVERLAY NAME                   FILES/DIRS&lt;/span&gt;
&lt;span class="go"&gt;rocky-8-split                  /etc/&lt;/span&gt;
&lt;span class="go"&gt;rocky-8-split                  /etc/fstab.ww&lt;/span&gt;

&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split&lt;span class="w"&gt; &lt;/span&gt;/etc/fstab.ww&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tail&lt;span class="w"&gt; &lt;/span&gt;-n1
&lt;span class="go"&gt;{{ .Ipaddr }}:/var/lib/warewulf/chroots/rocky-8-split/rootfs/opt /opt nfs defaults 0 0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I can add the new overlay to our &lt;code&gt;wwinit&lt;/code&gt; list, and the &lt;code&gt;fstab&lt;/code&gt; in
&lt;code&gt;rocky-8-split&lt;/code&gt; will override the one in &lt;code&gt;wwinit&lt;/code&gt;. (Note: &lt;code&gt;--wwinit&lt;/code&gt;
was specified as &lt;code&gt;--system&lt;/code&gt; in Warewulf 4.3.0.)&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;profile&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--wwinit&lt;span class="w"&gt; &lt;/span&gt;wwinit,rocky-8-split&lt;span class="w"&gt; &lt;/span&gt;default
&lt;span class="gp"&gt;[root@wwctl1 ~]# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;profile&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--container&lt;span class="w"&gt; &lt;/span&gt;rocky-8-split&lt;span class="w"&gt; &lt;/span&gt;default
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;From a compute node, we can see that &lt;code&gt;/opt&lt;/code&gt; is mounted via NFS as
expected.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@compute1 ~]# &lt;/span&gt;findmnt&lt;span class="w"&gt; &lt;/span&gt;/opt
&lt;span class="go"&gt;TARGET SOURCE                                                      FSTYPE OPTIONS&lt;/span&gt;
&lt;span class="go"&gt;/opt   10.0.0.3:/var/lib/warewulf/chroots/rocky-8-split/rootfs/opt nfs4   rw,relatime,vers=4.2,rsize=262144,wsize=262144,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.0.0.4,local_lock=none,addr=10.0.0.3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can further confirm that &lt;code&gt;/opt&lt;/code&gt; is empty on the local, PXE-deployed
file system.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;[root@compute1 ~]# &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;/mnt
&lt;span class="gp"&gt;[root@compute1 ~]# &lt;/span&gt;du&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;/mnt/opt
&lt;span class="go"&gt;0   /mnt/opt&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Future work&lt;/h3&gt;
&lt;p&gt;As demonstrated here, we can already implement split PXE/NFS images
using functionality already in Warewulf; but future Warewulf
development may simplify this process further:&lt;/p&gt;
&lt;h4&gt;Container path variables in warewulf.conf&lt;/h4&gt;
&lt;p&gt;We could support referring to compute node images in
&lt;code&gt;warewulf.conf&lt;/code&gt;. For example, it would be nice to be able to replace&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nt"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;export paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/var/lib/warewulf/chroots/rocky-8-split/rootfs/opt&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;export options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rw,sync,no_root_squash&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;with something like&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nt"&gt;nfs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;export paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;containers&lt;/span&gt;&lt;span class="p p-Indicator"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'rocky-8-split'&lt;/span&gt;&lt;span class="p p-Indicator"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;}}&lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/opt&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;export options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rw,sync,no_root_squash&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, our configuration would not have to hard-code the path to
the container chroot.&lt;/p&gt;
&lt;h4&gt;Move NFS mount settings to nodes and profiles&lt;/h4&gt;
&lt;p&gt;Right now, NFS client settings are stored in &lt;code&gt;warewulf.conf&lt;/code&gt; as &lt;code&gt;mount
options&lt;/code&gt;, &lt;code&gt;mount&lt;/code&gt;, and implicitly via &lt;code&gt;path&lt;/code&gt;; but if these settings
were moved to nodes and profiles we could configure per-profile and
per-node NFS client behavior without having to manually edit or
override &lt;code&gt;fstab&lt;/code&gt;.&lt;/p&gt;</description><category>technology</category><category>warewulf</category><guid>urn:uuid:0f31138a-a5e0-467b-be30-c9287d505ec9</guid><pubDate>Wed, 07 Dec 2022 23:30:00 GMT</pubDate></item><item><title>Stateless provisioning of stateful nodes: examples with Warewulf 4</title><link>https://civilfritz.net/anderbubble/stateless-provisioning-of-stateful-nodes/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;&lt;em&gt;&lt;a href="https://ciq.co/blog/stateless-provisioning-of-stateful-notes-examples-with-warewulf-4/"&gt;This article was also published via the CIQ blog on 30 November 2022.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When deploying Warewulf 4, we often encounter expectations that
Warewulf should support stateful provisioning. Typically these
expectations are born from experience with another system (such as
Foreman, XCAT, or even Warewulf 3) that supported writing a
provisioned operating system to the local disk of each compute node.&lt;/p&gt;
&lt;p&gt;Warewulf 4 intentionally omits this kind of stateful provisioning from
its feature set, following experiences from Warewulf 3: the code for
stateful provisioning was complex, and required a disproportionate
amount of maintenance compared to the number of sites using it.&lt;/p&gt;
&lt;p&gt;For the most part, we think that arguments for stateful provisioning
are better addressed within Warewulf 4's stateless provisioning
process. I'd like to go over three such common use cases here, and
show how each can be addressed to provision nodes with local state
using Warewulf 4.&lt;/p&gt;
&lt;h3&gt;Local scratch&lt;/h3&gt;
&lt;p&gt;The first thing to understand is that stateless provisioning does not
mean diskless nodes. For example, you may have a local disk that you
want to provide as a scratch file system.&lt;/p&gt;
&lt;p&gt;Warewulf compute nodes run a small &lt;code&gt;wwclient&lt;/code&gt; agent that assists with
the &lt;code&gt;init&lt;/code&gt; process during boot and deploys the node's overlays during
boot and runtime. &lt;code&gt;wwclient&lt;/code&gt; reads its own initialization scripts from
&lt;code&gt;/warewulf/init.d/&lt;/code&gt;, so we can place startup scripts there to take
actions during boot.&lt;/p&gt;
&lt;p&gt;My test nodes here are KVM instances with a virtual disk at
&lt;code&gt;/dev/vda&lt;/code&gt;. This &lt;code&gt;wwclient&lt;/code&gt; init script looks for a "local-scratch"
file system and, if it does not exist, creates one on the local disk.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# /warewulf/init.d/70-mkfs-local-scratch&lt;/span&gt;

&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/sbin:/usr/bin

&lt;span class="c1"&gt;# KVM disks require a kernel module&lt;/span&gt;
modprobe&lt;span class="w"&gt; &lt;/span&gt;virtio_blk

&lt;span class="nv"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-scratch&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"local-scratch filesystem already exists: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/vda
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-scratch filesystem on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkfs.ext4&lt;span class="w"&gt; &lt;/span&gt;-FL&lt;span class="w"&gt; &lt;/span&gt;local-scratch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;wwclient&lt;/code&gt; runs this script before it passes &lt;code&gt;init&lt;/code&gt; on to systemd, so
it is also processed before &lt;code&gt;fstab&lt;/code&gt;. So we can mount the
"local-scratch" file system just like any other disk in &lt;code&gt;fstab&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;LABEL=local-scratch /mnt/scratch ext4 defaults,X-mount.mkdir,nofail 0 0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The Warewulf 4 overlay system allows us to deploy customized files to
nodes or groups of nodes (via profiles) at boot. For this example,
I've placed my customized &lt;code&gt;fstab&lt;/code&gt; and init script in a "local-scratch"
overlay and included it as a system overlay, alongside the default
&lt;code&gt;wwinit&lt;/code&gt; overlay.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;local-scratch
&lt;span class="go"&gt;OVERLAY NAME                   FILES/DIRS  &lt;/span&gt;
&lt;span class="go"&gt;local-scratch                  /etc/        &lt;/span&gt;
&lt;span class="go"&gt;local-scratch                  /etc/fstab.ww&lt;/span&gt;
&lt;span class="go"&gt;local-scratch                  /warewulf/   &lt;/span&gt;
&lt;span class="go"&gt;local-scratch                  /warewulf/init.d/&lt;/span&gt;
&lt;span class="go"&gt;Local-scratch                  /warewulf/init.d/70-mkfs-local-scratch&lt;/span&gt;

&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;profile&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--system&lt;span class="w"&gt; &lt;/span&gt;wwinit,local-scratch&lt;span class="w"&gt; &lt;/span&gt;default
&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;build
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because &lt;code&gt;local-scratch&lt;/code&gt; is listed after &lt;code&gt;wwinit&lt;/code&gt; in the "system"
overlay list (see above), its &lt;code&gt;fstab&lt;/code&gt; overrides the definition in the
&lt;code&gt;wwinit&lt;/code&gt; overlay. &lt;code&gt;70-mkfs-local-scratch&lt;/code&gt; is placed alongside other
init scripts, and is processed in lexical order.&lt;/p&gt;
&lt;p&gt;A node booting with this overlay will create (if it does not exist) a
"local-scratch" file system and mount it at "/mnt/scratch",
potentially for use by compute jobs.&lt;/p&gt;
&lt;h3&gt;Disk partitioning&lt;/h3&gt;
&lt;p&gt;But perhaps you want to do something more complex. Perhaps you have a
single disk, but you want to allocate part of it for scratch (as
above) and part of it as swap space. Perhaps contrary to popular
opinion, we actively encourage the use of swap space in an
image-netboot environment like Warewulf 4: a swap partition that is at
least as big as the image to be booted allows Linux to write idle
portions of the image to disk, freeing up system memory for compute
jobs.&lt;/p&gt;
&lt;p&gt;So let's expand on the above pattern to actually &lt;em&gt;partition&lt;/em&gt; a disk,
rather than just format it.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# /warewulf/init.d/70-parted&lt;/span&gt;

&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/sbin:/usr/bin

&lt;span class="c1"&gt;# KVM disks require a kernel module&lt;/span&gt;
modprobe&lt;span class="w"&gt; &lt;/span&gt;virtio_blk

&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-swap&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-scratch&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found local-swap: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found local-scratch: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/vda
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;2"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Writing partition table to &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;parted&lt;span class="w"&gt; &lt;/span&gt;--script&lt;span class="w"&gt; &lt;/span&gt;--align&lt;span class="o"&gt;=&lt;/span&gt;optimal&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mklabel&lt;span class="w"&gt; &lt;/span&gt;gpt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;linux-swap&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2GB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;ext4&lt;span class="w"&gt; &lt;/span&gt;2GB&lt;span class="w"&gt; &lt;/span&gt;-1

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-swap on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkswap&lt;span class="w"&gt; &lt;/span&gt;--label&lt;span class="o"&gt;=&lt;/span&gt;local-swap&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-scratch on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkfs.ext4&lt;span class="w"&gt; &lt;/span&gt;-FL&lt;span class="w"&gt; &lt;/span&gt;local-scratch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This new init script looks for the expected "local-scratch" and
"local-swap" and, if either of them is not found, uses &lt;code&gt;parted&lt;/code&gt; to
partition the disk and creates them. As before, this is done before
&lt;code&gt;fstab&lt;/code&gt; is processed, so we can configure these with &lt;code&gt;fstab&lt;/code&gt; the
standard way.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;LABEL=local-swap swap swap defaults,nofail 0 0
LABEL=local-scratch /mnt/scratch ext4 defaults,X-mount.mkdir,nofail 0 0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This configuration went into a new &lt;code&gt;parted&lt;/code&gt; overlay, allowing us to
configure some nodes for "local-scratch" only, and some nodes for this
partitioned layout.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;parted
&lt;span class="go"&gt;OVERLAY NAME                   FILES/DIRS  &lt;/span&gt;
&lt;span class="go"&gt;parted                         /etc/        &lt;/span&gt;
&lt;span class="go"&gt;parted                         /etc/fstab.ww&lt;/span&gt;
&lt;span class="go"&gt;parted                         /warewulf/   &lt;/span&gt;
&lt;span class="go"&gt;parted                         /warewulf/init.d/&lt;/span&gt;
&lt;span class="go"&gt;parted                         /warewulf/init.d/70-parted&lt;/span&gt;

&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;profile&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--system&lt;span class="w"&gt; &lt;/span&gt;wwinit,parted&lt;span class="w"&gt; &lt;/span&gt;default
&lt;span class="gp"&gt;# &lt;/span&gt;wwctl&lt;span class="w"&gt; &lt;/span&gt;overlay&lt;span class="w"&gt; &lt;/span&gt;build
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(Note: I installed &lt;code&gt;parted&lt;/code&gt; in my system image to support this; but
the same could also be done with &lt;code&gt;sfdisk&lt;/code&gt;, which is included in the
image by default.)&lt;/p&gt;
&lt;h3&gt;Persistent storage for logs&lt;/h3&gt;
&lt;p&gt;Another common use case we hear concerns the persistence of logs on
the compute nodes. Particularly in a failure event, where a node must
be rebooted, it can be useful to have retained logs on the compute
host so that they can be investigated when the node is brought back
up: in a default stateless deployment, these logs are lost on reboot.&lt;/p&gt;
&lt;p&gt;We can extend from the previous two examples to deploy a "local-log"
file system to retain these logs between reboots.&lt;/p&gt;
&lt;p&gt;(Note: generally we advise &lt;em&gt;not&lt;/em&gt; retaining logs on compute nodes: in
stead, you should deploy something like Elasticsearch, Splunk, or even
just a central rsyslog instance.)&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="ch"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# /warewulf/init.d/70-parted&lt;/span&gt;

&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/sbin:/usr/bin

&lt;span class="c1"&gt;# KVM disks require a kernel module&lt;/span&gt;
modprobe&lt;span class="w"&gt; &lt;/span&gt;virtio_blk

&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-swap&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-log&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;findfs&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LABEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;local-scratch&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found local-swap: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found local-log: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Found local-scratch: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/vda
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;2"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;3"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Writing partition table to &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;parted&lt;span class="w"&gt; &lt;/span&gt;--script&lt;span class="w"&gt; &lt;/span&gt;--align&lt;span class="o"&gt;=&lt;/span&gt;optimal&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;disk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mklabel&lt;span class="w"&gt; &lt;/span&gt;gpt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;linux-swap&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;2GB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;ext4&lt;span class="w"&gt; &lt;/span&gt;2GB&lt;span class="w"&gt; &lt;/span&gt;4GB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;ext4&lt;span class="w"&gt; &lt;/span&gt;4GB&lt;span class="w"&gt; &lt;/span&gt;-1

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-swap on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkswap&lt;span class="w"&gt; &lt;/span&gt;--label&lt;span class="o"&gt;=&lt;/span&gt;local-swap&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_swap&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-log on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkfs.ext4&lt;span class="w"&gt; &lt;/span&gt;-FL&lt;span class="w"&gt; &lt;/span&gt;local-log&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Populating local-log from image /var/log/"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/log/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_log&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/log&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rsync&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;/var/log/&lt;span class="w"&gt; &lt;/span&gt;/mnt/log/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/log/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;rmdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/log

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Creating local-scratch on &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkfs.ext4&lt;span class="w"&gt; &lt;/span&gt;-FL&lt;span class="w"&gt; &lt;/span&gt;local-scratch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;local_scratch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For the most part, this follows the same pattern from the "parted"
example above; but adds a step to initalize the new "local-log" file
system from the directory structure in the image.&lt;/p&gt;
&lt;p&gt;Finally, the new file system is added to fstab, after which logs will
be persisted on the local disk.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;LABEL=local-swap swap swap defaults,nofail 0 0
LABEL=local-scratch /mnt/scratch ext4 defaults,X-mount.mkdir,nofail 0 0
LABEL=local-log /var/log ext4 defaults,nofail 0 0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some applications may write logs outside of &lt;code&gt;/var/log&lt;/code&gt;; but, in these
instances, it's probably easier to configure the application to write
to &lt;code&gt;/var/log&lt;/code&gt; than to try to capture all the places where logs might
be written.&lt;/p&gt;
&lt;h3&gt;The future&lt;/h3&gt;
&lt;p&gt;There are a few more use cases that we sometimes hear brought up in
the context of stateful node provisioning:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can we use Ansible to configure compute nodes?&lt;/li&gt;
&lt;li&gt;How can we configure custom kernels and kernel modules per node?&lt;/li&gt;
&lt;li&gt;Isn't stateless provisioning slower than having the OS deployed on
  disk?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you'd like to hear more about these or other potential corner-cases
for stateless provisioning, &lt;a href="https://ciq.co/contact-us"&gt;get in touch!&lt;/a&gt; We'd love to
hear from you, learn about the work you're doing, and address any of
the challenges you're having.&lt;/p&gt;</description><category>technology</category><category>warewulf</category><guid>urn:uuid:f6dd7a5c-d8ad-43a8-a38c-e982ec2e1637</guid><pubDate>Fri, 21 Oct 2022 06:00:00 GMT</pubDate></item><item><title>Warewulf Deep Dive | CIQ Webinar</title><link>https://civilfritz.net/anderbubble/2022-07-07-warewulf-deep-dive-ciq-webinar/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;I'm really excited about Warewulf 4! I'm also really excited about
OpenHPC, Apptainer, and containerizing HPC workloads, particularly
MPI. Today I presented my most recent work in these areas in the CIQ
webinar.&lt;/p&gt;
&lt;div class="youtube-video"&gt;
&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/EpVDeesAq4c?rel=0&amp;amp;wmode=transparent" frameborder="0" allow="encrypted-media" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;p&gt;We're doing these webinars every week! I hope you'll join us Thursdays
at 11:00 Pacific time, either via &lt;a class="reference external" href="https://www.youtube.com/c/CtrlIQ"&gt;YouTube&lt;/a&gt; and &lt;a class="reference external" href="https://www.linkedin.com/company/ctrliq/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;</description><guid>urn:uuid:8e4076ce-e6b4-4f7f-9e9d-98d48ff85d09</guid><pubDate>Thu, 07 Jul 2022 21:58:59 GMT</pubDate></item><item><title>Twilight Princess | Game Older</title><link>https://civilfritz.net/gameolder/twilight-princess/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;a class="reference external image-reference" href="https://civilfritz.net/gameolder/twilight-princess/sword-and-shield.png"&gt;&lt;img alt="Family heirlooms of the adult gamer." class="align-right" src="https://civilfritz.net/gameolder/twilight-princess/sword-and-shield.thumbnail.png" style="width: 20em;"&gt;&lt;/a&gt;
&lt;p&gt;Andi beat a game! And it's a Zelda game! Come hang with us for a bit
while we discuss the special place the Zelda series has in each of our
hearts.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class="audio-embed"&gt;
  &lt;div&gt;
    &lt;audio controls preload="metadata"&gt;
      &lt;source src="https://objects.civilfritz.net/podcasts/gameolder/2022-06-18-twilight-princess.mp3" type="audio/mpeg"&gt;&lt;/source&gt;
        &lt;div&gt;
          &lt;a href="https://objects.civilfritz.net/podcasts/gameolder/2022-06-18-twilight-princess.mp3"&gt;download&lt;/a&gt;
        &lt;/div&gt;
    &lt;/audio&gt;
&lt;/div&gt;

&lt;section id="media-used"&gt;
&lt;h2&gt;Media used&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=koIu848pZO0"&gt;Title Theme (Ocarina of Time) Guitar Cover | DSC&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://www.youtube.com/watch?v=nSANm-9iV1Y"&gt;Link's Awakening Medley (from The Legend of Zelda: Concert 2018) | Tokyo Philharmonic Orchestra&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also included is background music and marketing from a variety of
Zelda games, including Link to the Past, Twilight Princess, and Breath
of the Wild.&lt;/p&gt;
&lt;/section&gt;&lt;/div&gt;</description><category>podcast-episode</category><enclosure url="https://objects.civilfritz.net/podcasts/gameolder/2022-06-18-twilight-princess.mp3" length="0" type="audio/mpeg"></enclosure><guid>urn:uuid:34f6368a-3742-47c1-88e8-8e417c82c903</guid><pubDate>Sat, 18 Jun 2022 06:00:00 GMT</pubDate></item><item><title>Apptainer Signatures | CIQ Webinar</title><link>https://civilfritz.net/anderbubble/2022-05-19-apptainer-signatures-ciq-webinar/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;I had the pleasure of participating in my first CIQ webinar today!
Check it out if you'd like to learn a bit about Apptainer's support
for cryptographic signatures, using well-established PGP
infrastructure and paradigms.&lt;/p&gt;
&lt;div class="youtube-video"&gt;
&lt;iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/qODlcbeVW?rel=0&amp;amp;wmode=transparent" frameborder="0" allow="encrypted-media" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;p&gt;I hope you'll join us for our next session! We're live every Thursday
at 11:00 Pacific time, streaming to &lt;a class="reference external" href="https://www.youtube.com/c/CtrlIQ"&gt;YouTube&lt;/a&gt; and &lt;a class="reference external" href="https://www.linkedin.com/company/ctrliq/"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;</description><guid>urn:uuid:d647da7c-0447-476f-8fe8-23525d1b4f69</guid><pubDate>Thu, 19 May 2022 19:25:28 GMT</pubDate></item><item><title>Ancient Astronauts | Scifi Camp with Andrew Sears</title><link>https://civilfritz.net/anderbubble/2022-04-25-scifi-camp-ancient-astronauts/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;a class="reference external image-reference" href="https://civilfritz.net/anderbubble/2022-04-25-scifi-camp-ancient-astronauts/scifi-camp.jpg"&gt;&lt;img alt="Astro Warrior box art" src="https://civilfritz.net/anderbubble/2022-04-25-scifi-camp-ancient-astronauts/scifi-camp.thumbnail.jpg" style="width: 200px;"&gt;&lt;/a&gt;
&lt;p&gt;I had a lot of fun talking about &lt;a class="reference external" href="https://www.scifi.camp/1886039/10505090-ancient-astronauts"&gt;Ancient Astronauts&lt;/a&gt; with my good friend
Andrew Sears on his new podcast series &lt;a class="reference external" href="https://www.scifi.camp"&gt;Scifi Camp&lt;/a&gt;. We talk about Stargate,
Mass Effect, and the fuzzy boundary with faith and religion. I hope you enjoy
it as much as I did!&lt;/p&gt;</description><guid>urn:uuid:8cf79172-2a54-45db-90fe-72cffeaf699d</guid><pubDate>Mon, 25 Apr 2022 06:00:00 GMT</pubDate></item><item><title>Migrating to LDAP PAM Pass Through Auth</title><link>https://civilfritz.net/changelog/ldap-pam-passthrough/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;The Research Computing authentication path is more complex than I'd
like.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;We start with &lt;code class="docutils literal"&gt;pam_sss&lt;/code&gt; which, of course, authenticates against
sssd.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Because we have users from multiple home institutions, both internal
and external, sssd is configured with multiple domains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Two of our configured domains authenticate against Duo and Active
Directory. To support this we run two discrete instances of the &lt;a class="reference external" href="https://duo.com/docs/authproxy-reference"&gt;Duo
authentication proxy&lt;/a&gt;, one for each domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Duo authentication proxy can present either an LDAP or RADIUS
interface. We went with RADIUS. So sssd is configured with
&lt;code class="docutils literal"&gt;auth_provider = proxy&lt;/code&gt;, with a discrete pam stack for each
domain. This pam stack uses &lt;code class="docutils literal"&gt;pam_radius&lt;/code&gt; to authenticate against
the correct Duo authentication proxy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The relevant Duo authentication proxy then performs AD
authentication to the relevant authoritative domain and, on success,
performs Duo authentication for second factor.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of this technically works, and has been working for some
time. However, we've increasingly seen a certain bug in sssd's
&lt;code class="docutils literal"&gt;proxy&lt;/code&gt; authentication provider, which manifests as an incorrect
monitoring or management of authentication threads.&lt;/p&gt;
&lt;section id="the-problem"&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;pre class="literal-block"&gt;[sssd[be[rc.colorado.edu]]] [dp_attach_req] (0x0400): Number of active DP request: 32&lt;/pre&gt;
&lt;p&gt;sssd maintains a number of pre-forked children for performing this
proxy authentication. This default to 10 threads, and is configurable
per-domain as &lt;code class="docutils literal"&gt;proxy_max_children&lt;/code&gt;. Somewhere in sssd a bug exists
that either prevents threads from being closed properly or fails to
decrement the active thread count when they are closed. When the
"Number of active DP request" exceeds &lt;code class="docutils literal"&gt;proxy_max_children&lt;/code&gt; sssd will
no longer perform authentication for the affected domain.&lt;/p&gt;
&lt;p&gt;We have &lt;a class="reference external" href="https://access.redhat.com/support/cases/#/case/02704264"&gt;reported this issue to Red Hat&lt;/a&gt;, but 8 months on and we
still don't have a fix. Meanwhile, I'm interested in simplifying our
authentication path, hopefully removing the &lt;code class="docutils literal"&gt;proxy&lt;/code&gt; authentication
provider from our configuration in the process, and making sssd
optional for authentication in our environment.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="our-solution"&gt;
&lt;h2&gt;Our solution&lt;/h2&gt;
&lt;p&gt;We use &lt;a class="reference external" href="https://directory.fedoraproject.org/"&gt;389 Directory Server&lt;/a&gt; as our local LDAP server. 389 has with
it &lt;a class="reference external" href="https://directory.fedoraproject.org/docs/389ds/howto/howto-pam-pass-through.html"&gt;the capability to proxy authentication via PAM&lt;/a&gt;. A previous
generation RC LDAP used this to perform authentication; but only in a
way that supported a single authentication path. However, with some
research and experimentation, we have managed to configure our
instance with different proxy authentication paths for each of our
child domains.&lt;/p&gt;
&lt;p&gt;First we simply activate the &lt;code class="docutils literal"&gt;PAM Pass Through Auth&lt;/code&gt; plugin by
setting &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;nsslapd-pluginEnabled:&lt;/span&gt; on&lt;/code&gt; in the existing LDAP entry.&lt;/p&gt;
&lt;pre class="literal-block"&gt;dn: cn=PAM Pass Through Auth,cn=plugins,cn=config
objectClass: top
objectClass: nsSlapdPlugin
objectClass: extensibleObject
objectClass: pamConfig
cn: PAM Pass Through Auth
nsslapd-pluginPath: libpam-passthru-plugin
nsslapd-pluginInitfunc: pam_passthruauth_init
nsslapd-pluginType: betxnpreoperation
nsslapd-pluginEnabled: on
nsslapd-pluginloadglobal: true
nsslapd-plugin-depends-on-type: database
pamMissingSuffix: ALLOW
pamExcludeSuffix: cn=config
pamIDMapMethod: RDN
pamIDAttr: uid
pamFallback: FALSE
pamSecure: TRUE
pamService: ldapserver
nsslapd-pluginId: pam_passthruauth
nsslapd-pluginVersion: 1.3.4.0
nsslapd-pluginVendor: 389 Project
nsslapd-pluginDescription: PAM pass through authentication plugin&lt;/pre&gt;
&lt;p&gt;The specifics of authentication can be specified at this level as
well, if we're able to express our desired behavior in a single
configuration. However, the plugin supports multiple simultaneous
configurations expressed as nested LDAP entries.&lt;/p&gt;
&lt;pre class="literal-block"&gt;dn: cn=colorado.edu PAM,cn=PAM Pass Through Auth,cn=plugins,cn=config
objectClass: pamConfig
objectClass: top
cn: colorado.edu PAM
pamMissingSuffix: ALLOW
pamExcludeSuffix: cn=config
pamIDMapMethod: RDN ENTRY
pamIDAttr: uid
pamFallback: FALSE
pamSecure: TRUE
pamService: curc-twofactor-duo
pamFilter: (&amp;amp;(objectClass=posixAccount)(!(homeDirectory=/home/*@*)))


dn: cn=colostate.edu PAM,cn=PAM Pass Through Auth,cn=plugins,cn=config
objectClass: pamConfig
objectClass: top
cn: colostate.edu PAM
pamMissingSuffix: ALLOW
pamExcludeSuffix: cn=config
pamIDMapMethod: RDN ENTRY
pamIDAttr: uid
pamFallback: FALSE
pamSecure: TRUE
pamService: csu
pamFilter: (&amp;amp;(objectClass=posixAccount)(homeDirectory=/home/*@colostate.edu))&lt;/pre&gt;
&lt;p&gt;Our two sets of users are authenticated using different PAM stacks, as
before. Only now this proxy authentication is happening within the
LDAP server, rather than within sssd. This may seem like a small
difference, but there are multiple benefits:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;The proxy configuration exists, and need only be maintained, only
within the LDAP server. It does not require all login nodes to run
sssd and a complex, multi-tiered PAM stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The LDAP "PAM Pass Through Auth" plugin does not have the same bug
as the sssd &lt;code class="docutils literal"&gt;proxy&lt;/code&gt; authentication method, bypassing our immediate
problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applications that do not support PAM authentication, such as XDMoD,
Foreman, and Grafana, can now be configured with simple LDAP
authentication, and need not know anything of the complexity of
authenticating our multiple domains.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For now I'm differentiating our different user types based on the name
of their home directory, because it happens to include the relevant
domain suffix. In the future we expect to update usernames in the
directory to match and would then likely update this configuration to
use &lt;code class="docutils literal"&gt;uid&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="cleaning-up-a-few-remaining-issues"&gt;
&lt;h2&gt;Cleaning up a few remaining issues&lt;/h2&gt;
&lt;p&gt;However, when I first tied this back into sssd, I DOS'd our LDAP
server.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[domain/rc.colorado.edu]
debug_level = 3

description = CU Boulder Research Computing
id_provider = ldap
auth_provider = ldap
chpass_provider = none

min_id=1000
enumerate = false
entry_cache_timeout = 300

ldap_id_use_start_tls = True
ldap_tls_reqcert = allow
ldap_uri = ldap://ldap.rc.int.colorado.edu
ldap_search_base = dc=rc,dc=int,dc=colorado,dc=edu
ldap_user_search_base = ou=UCB,ou=People,dc=rc,dc=int,dc=colorado,dc=edu
ldap_group_search_base = ou=UCB,ou=Groups,dc=rc,dc=int,dc=colorado,dc=edu&lt;/pre&gt;
&lt;p&gt;This seemed simple enough: when I would try to authenticate using this
configuration, I would enter my password as usual and then respond to
a Duo "push." But the authentication never cleared in sssd, and I
would keep receiving Duo pushes until I stopped sssd. This despite the
fact that I could authenticate with &lt;code class="docutils literal"&gt;ldapsearch&lt;/code&gt; as expected.&lt;/p&gt;
&lt;pre class="literal-block"&gt;$ ldapsearch -LLL -x -ZZ -D uid=[redacted],ou=UCB,ou=People,dc=rc,dc=int,dc=colorado,dc=edu -W '(uid=[redacted])' dn
Enter LDAP Password:
dn: uid=[redacted],ou=UCB,ou=People,dc=rc,dc=int,dc=colorado,dc=edu&lt;/pre&gt;
&lt;p&gt;I eventually discovered that sssd has a six-second timeout for "calls
to synchronous LDAP APIs," including BIND. This timeout is entirely
reasonable--even generous--for operations that do not have a manual
intervention component. But when BIND includes time to send a
notification to a phone, unlock the phone, and acknowledge the
notification in an app, it is easy to exceed this timeout. sssd gives
up and tries again, prompting a new push that won't be received until
the first is addressed. In this way, the timeouts just extend against
each other.&lt;/p&gt;
&lt;p&gt;Thankfully, this timeout is also configurable as &lt;code class="docutils literal"&gt;ldap_opt_timeout&lt;/code&gt;
in the relevant sssd domain section. I went with &lt;code class="docutils literal"&gt;ldap_opt_timeout =
90&lt;/code&gt;, which is likely longer than anyone will need.&lt;/p&gt;
&lt;p&gt;There is still the matter of the fact that this DOS'd the LDAP server,
however. I suspect I had exhausted &lt;a class="reference external" href="https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html/performance_tuning_guide/ds-threads"&gt;the number of directory server
threads&lt;/a&gt; with pending, long-living (due to manual intervention
required / timeout) BIND requests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The number of threads Directory Server uses to handle simultaneous
connections affects the performance of the server. For example, if
all threads are busy handling time-consuming tasks (such as add
operations), new incoming connections are queued until a free
thread can process the request.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Red Hat suggests that &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;nsslapd-threadnumber&lt;/span&gt;&lt;/code&gt; should be 32 for an
eight-CPU system like ours; so for now I simply increased to this
recommendation from 16. If we continue to experience thread exhaustion
in real-world use, we can always increase the number of threads again.&lt;/p&gt;
&lt;/section&gt;</description><guid>urn:uuid:2fb28fc3-59aa-4d3d-b4d2-820af5c9942f</guid><pubDate>Thu, 18 Mar 2021 06:00:00 GMT</pubDate></item><item><title>digging into BeeGFS striping</title><link>https://civilfritz.net/changelog/digging-into-beegfs-striping/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;I did some work today figuring out how BeeGFS actually writes its data
to disk. I shudder to think that we’d actually use this knowledge; but
I still found it interesting, so I want to share.&lt;/p&gt;
&lt;p&gt;First, I created a simple striped file in the rcops allocation.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# beegfs-ctl --createfile testfile --numtargets=2 --storagepoolid=2
Operation succeeded.&lt;/pre&gt;
&lt;p&gt;This file will stripe across two targets (chosen by BeeGFS at random)
and is using the default 1M chunksize for the rcops storage pool. You
can see this with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;beegfs-ctl&lt;/span&gt; &lt;span class="pre"&gt;--getentryinfo&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# beegfs-ctl --getentryinfo /mnt/beegfs/rcops/testfile --verbose
EntryID: 9-5F7E8E87-1
Metadata buddy group: 1
Current primary metadata node: bmds1 [ID: 1]
Stripe pattern details:
+ Type: RAID0
+ Chunksize: 1M
+ Number of storage targets: desired: 2; actual: 2
+ Storage targets:
  + 826 @ boss1 [ID: 1]
  + 834 @ boss2 [ID: 2]
Chunk path: uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1
Dentry path: 50/4/0-5BEDEB51-1/&lt;/pre&gt;
&lt;p&gt;I write an easily-recognized dataset to the file: 1M of &lt;code class="docutils literal"&gt;A&lt;/code&gt; to the
file; then 1M of &lt;code class="docutils literal"&gt;B&lt;/code&gt; and so-on.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# python -c 'import sys; sys.stdout.write("A"*(1024*1024))' &amp;gt;&amp;gt;testfile
[root@boss2 rcops]# python -c 'import sys; sys.stdout.write("B"*(1024*1024))' &amp;gt;&amp;gt;testfile
[root@boss2 rcops]# python -c 'import sys; sys.stdout.write("C"*(1024*1024))' &amp;gt;&amp;gt;testfile
[root@boss2 rcops]# python -c 'import sys; sys.stdout.write("D"*(1024*1024))' &amp;gt;&amp;gt;testfile&lt;/pre&gt;
&lt;p&gt;This gives me a 4M file, precisely 1024*1024*4=4194304 bytes.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# du --bytes --apparent-size testfile
4194304     testfile&lt;/pre&gt;
&lt;p&gt;Those two chunk files, as identified by &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;beegfs-ctl&lt;/span&gt; &lt;span class="pre"&gt;--getentryinfo&lt;/span&gt;&lt;/code&gt;,
are at
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/data/boss207/rcops/storage/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1&lt;/span&gt;&lt;/code&gt;
and
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/data/boss106/rcops/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1&lt;/span&gt;&lt;/code&gt;. (&lt;code class="docutils literal"&gt;boss106/rcops&lt;/code&gt;
doesn’t have a storage directory as part of an experiment to see how
difficult it would be to remove them. I guess we never put it back.)
the &lt;code class="docutils literal"&gt;boss1&lt;/code&gt; target, &lt;code class="docutils literal"&gt;826&lt;/code&gt;, is first in the list, so that’s where
the file starts.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss1 ~]# dd if=/data/boss106/rcops/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1 bs=1 count=5 status=none
AAAAA&lt;/pre&gt;
&lt;p&gt;if we skip 1M (1024*1024 bytes) we see that that’s where the file
changes to &lt;code class="docutils literal"&gt;C&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss1 ~]# dd if=/data/boss106/rcops/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1 bs=1 skip=$(((1024 * 1024))) count=5 status=none&lt;/pre&gt;
&lt;p&gt;And we can see that actually is precisely where it starts by stepping
back a little.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss1 ~]# dd if=/data/boss106/rcops/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1 bs=1 skip=$(((1024 * 1024)-2)) count=5 status=none
AACCC&lt;/pre&gt;
&lt;p&gt;Cool. So we’ve found the end of the first chunk (made of &lt;code class="docutils literal"&gt;A&lt;/code&gt;) and
the start of the third chunk (made of &lt;code class="docutils literal"&gt;C&lt;/code&gt;). That means the second
and fourth chunks are over in 834. Which they are.&lt;/p&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# dd if=/data/boss207/rcops/storage/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1 bs=1 count=5 status=none
BBBBB&lt;/pre&gt;
&lt;pre class="literal-block"&gt;[root@boss2 rcops]# dd if=/data/boss207/rcops/storage/chunks/uF4240/5BED/E/0-5BEDEB51-1/9-5F7E8E87-1 bs=1 count=5 skip=$(((1024*1024-2))) status=none
BBDDD&lt;/pre&gt;
&lt;p&gt;So, in theory, if we wanted to bypass BeeGFS and re-construct files
from their chunks, we could do that. It sounds like a nightmare, but
we could do it. In a worst-case scenario.&lt;/p&gt;
&lt;p&gt;It’s this kind of transparency and inspectability that still makes me
really like BeeGFS, despite everything we’ve been through with it.&lt;/p&gt;</description><guid>urn:uuid:11d9d5b0-5b13-4b55-9479-1e46862331cf</guid><pubDate>Wed, 07 Oct 2020 06:00:00 GMT</pubDate></item><item><title>Wireguard on Raspberry Pi OS</title><link>https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;p&gt;Recently I fell victim to an attack on a security vulnerability in
SaltStack that left much of my homelab infected with
cryptominers. When I rebuilt the environment I found myself in the
market for a VPN solution.&lt;/p&gt;
&lt;p&gt;I have used OpenVPN for a little while, but I found it inconvenient
enough to set up and use that I only used it when absolutely necessary
to bridge between otherwise private networks.&lt;/p&gt;
&lt;p&gt;But I had been hearing good things about &lt;a class="reference external" href="https://www.wireguard.com/"&gt;WireGuard&lt;/a&gt;, so I performed
a test deployment. First between two disparate servers. Then on a
workstation. Then another. Each time the software deployed easily and
remained reliably available, particularly in contrast to the
unreliability I had become accustomed to with the Cisco VPN I use for
work.&lt;/p&gt;
&lt;p&gt;So I came to the last system in my network: a first-generation
Raspberry Pi B+. WireGuard isn't available in the Raspberry Pi OS (née
Raspbian) repository, but I found &lt;a class="reference external" href="https://engineerworkshop.com/blog/how-to-set-up-wireguard-on-a-raspberry-pi/"&gt;articles&lt;/a&gt; describing how to
install the packages from either Debian backports or unstable. I
generally avoid mixing distributions, but I followed the directions as
proof of concept.&lt;/p&gt;
&lt;p&gt;The base &lt;code class="docutils literal"&gt;wireguard&lt;/code&gt; package installed successfully, and little
surprise: it is a &lt;a class="reference external" href="https://github.com/dell/dkms"&gt;DKMS&lt;/a&gt; package, after all. However, binaries from
&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;wireguard-tools&lt;/span&gt;&lt;/code&gt; immediately segfaulted. (I expect this is because
the CPU in the first-generation B+ isn't supported by Debian.)&lt;/p&gt;
&lt;p&gt;But then I realized that APT makes source repositories as accessible
as binary repositories. Compiling my own WireGuard packages would
worry me less as well:&lt;/p&gt;
&lt;p&gt;First add the Debian Buster backports repository, including its
signing key. (You can verify the key fingerprint &lt;a class="reference external" href="https://ftp-master.debian.org/keys.html"&gt;at debian.org&lt;/a&gt;.)&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_9a94ebbcc5f84d5a896a17358e91055f-1" name="rest_code_9a94ebbcc5f84d5a896a17358e91055f-1" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_9a94ebbcc5f84d5a896a17358e91055f-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt-key&lt;span class="w"&gt; &lt;/span&gt;adv&lt;span class="w"&gt; &lt;/span&gt;--keyserver&lt;span class="w"&gt; &lt;/span&gt;keyserver.ubuntu.com&lt;span class="w"&gt; &lt;/span&gt;--recv-keys&lt;span class="w"&gt; &lt;/span&gt;0x80D15823B7FD1561F9F7BCDDDC30D7C23CBBABEE
&lt;a id="rest_code_9a94ebbcc5f84d5a896a17358e91055f-2" name="rest_code_9a94ebbcc5f84d5a896a17358e91055f-2" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_9a94ebbcc5f84d5a896a17358e91055f-2"&gt;&lt;/a&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'deb-src http://deb.debian.org/debian buster-backports main'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;tee&lt;span class="w"&gt; &lt;/span&gt;/etc/apt/sources.list.d/backports.list
&lt;a id="rest_code_9a94ebbcc5f84d5a896a17358e91055f-3" name="rest_code_9a94ebbcc5f84d5a896a17358e91055f-3" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_9a94ebbcc5f84d5a896a17358e91055f-3"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install the &lt;code class="docutils literal"&gt;devscripts&lt;/code&gt; package (so we can use &lt;code class="docutils literal"&gt;debuild&lt;/code&gt; to build
the WireGuard packages) and any build dependencies for WireGuard
itself.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_0638053b433a446582d8cca5acd25cb9-1" name="rest_code_0638053b433a446582d8cca5acd25cb9-1" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_0638053b433a446582d8cca5acd25cb9-1"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;devscripts
&lt;a id="rest_code_0638053b433a446582d8cca5acd25cb9-2" name="rest_code_0638053b433a446582d8cca5acd25cb9-2" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_0638053b433a446582d8cca5acd25cb9-2"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;build-dep&lt;span class="w"&gt; &lt;/span&gt;wireguard
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, download, build, and install WireGuard.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-1" name="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-1" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_f8532e3bf8594f6298ab3a000c09b7dd-1"&gt;&lt;/a&gt;apt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wireguard
&lt;a id="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-2" name="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-2" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_f8532e3bf8594f6298ab3a000c09b7dd-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wireguard-*&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;debuild&lt;span class="w"&gt; &lt;/span&gt;-us&lt;span class="w"&gt; &lt;/span&gt;-uc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-3" name="rest_code_f8532e3bf8594f6298ab3a000c09b7dd-3" href="https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/#rest_code_f8532e3bf8594f6298ab3a000c09b7dd-3"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;./wireguard_*.deb&lt;span class="w"&gt; &lt;/span&gt;./wireguard-tools_*.deb
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point you should have a fully functional WireGuard deployment,
with working &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;wireguard-tools&lt;/span&gt;&lt;/code&gt; binaries.&lt;/p&gt;</description><guid>https://civilfritz.net/changelog/wireguard-on-raspberry-pi-os/</guid><pubDate>Sun, 19 Jul 2020 06:00:00 GMT</pubDate></item><item><title>Ōkami | Game Older</title><link>https://civilfritz.net/gameolder/okami/</link><dc:creator>Jonathon Anderson</dc:creator><description>&lt;a class="reference external image-reference" href="https://civilfritz.net/gameolder/okami/okami.jpg"&gt;&lt;img alt="Issun must die" class="align-right" src="https://civilfritz.net/gameolder/okami/okami.thumbnail.jpg" style="width: 20em;"&gt;&lt;/a&gt;
&lt;p&gt;Jonathon was finally left with no excuse to not play Ōkami, when Cam
joined the crew, Steam library in tow. Like the game, we spend a lot
of time hanging out and talking too much.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;div class="audio-embed"&gt;
  &lt;div&gt;
    &lt;audio controls preload="metadata"&gt;
      &lt;source src="https://objects.civilfritz.net/podcasts/gameolder/2020-02-27-okami.mp3" type="audio/mpeg"&gt;&lt;/source&gt;
        &lt;div&gt;
          &lt;a href="https://objects.civilfritz.net/podcasts/gameolder/2020-02-27-okami.mp3"&gt;download&lt;/a&gt;
        &lt;/div&gt;
    &lt;/audio&gt;
&lt;/div&gt;

&lt;section id="media-used"&gt;
&lt;h2&gt;Media used&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Ōkami OST&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;"Harushiden," halc, ocremix.org&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="games-mentioned"&gt;
&lt;h2&gt;Games mentioned&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Ōkami&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Myst (series)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Death Stranding&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spyro the Dragon&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Witness&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Viewtiful Joe&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;God Hand&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vanquish&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Metal Gear Rising: Revengeance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Legend of Zelda: Ocarina of Time&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Legend of Zelda: Twilight Princess&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bayonetta&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nier: Automata&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Snake Pass&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Crash Bandicoot&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Katamari Damacy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chibi-Robo!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="other-references"&gt;
&lt;h2&gt;Other references&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://kotaku.com/sorry-i-dont-like-okami-1821329522"&gt;Sorry, I don't like Okami&lt;/a&gt;, Tim Rogers, Kotaku&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;&lt;/div&gt;</description><category>podcast-episode</category><enclosure url="https://objects.civilfritz.net/podcasts/gameolder/2020-02-27-okami.mp3" length="0" type="audio/mpeg"></enclosure><guid>urn:uuid:ac7064c0-9509-4c76-8e1c-206b193ff627</guid><pubDate>Thu, 27 Feb 2020 07:00:00 GMT</pubDate></item></channel></rss>