Estada.chhttps://estada.ch/2024-01-07T00:00:00+01:00Rust Engineering, Event-management, and Distributed SystemsWhy the pigeonhole principle prevents correct AI2024-01-07T00:00:00+01:002024-01-07T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2024-01-07:/2024/1/7/why-the-pigeonhole-principle-prevents-correct-ai/<p>The <a href="https://en.wikipedia.org/wiki/Pigeonhole_principle">pigeonhole prinicple</a> dictates that if we have seperate boxes or weights for each fact that …</p><p>The <a href="https://en.wikipedia.org/wiki/Pigeonhole_principle">pigeonhole prinicple</a> dictates that if we have seperate boxes or weights for each fact that we want to teach/store in an AI then we will need to grow the network with every fact that did not fit already.</p>
<p>Can we compress the data? If they are dependant, yes.</p>
<p>But that still leads to "random" facts like names of places and people.</p>
<h1>An example</h1>
<p>If I want the AI to remember corretly that I wrote this blog post and that my name is Stefan and that I posted it on <code>estada.ch</code>.</p>
<p>So, first we need the AI to overfit on this article and then on the my name (there is no reason for that particular name) and the domain (also completely random name that I liked).</p>
<p>This means that any AI needs at least 3 weights just to connect this article (that one alone is probably more than one weight) to correctly recognise these facts.</p>
<h2>Adding more and more facts</h2>
<p>This just increases the training time so this approach will become very expensive very quickly:</p>
<p><img alt="time to train vs number of weights" src="https://estada.ch/2024/1/7/why-the-pigeonhole-principle-prevents-correct-ai/2024-01-07_pigeonhole-vs-ai/time_to_train.png"></p>
<h1>A proposal</h1>
<p>I my opinion the future of a correct AI needs three things:</p>
<ol>
<li>Partition the AI into an intelligent part and a hard facts part</li>
<li>Show the enduser how certain an answer is</li>
<li>Show where the hard facts came from</li>
</ol>
<p>The hard facts can be multiple databases.
For example the phone book or DNS could be one source of truth.
An internal Wiki or encyclopedia could be another source.</p>
<p>Combining these sources then detecting conflicting information and highlighting them will allow a user to actually work with AI.</p>
<p>Because if we can not trust it, the only use cases are art and places where hallucinations are acceptable or even desired.</p>Manually move /bin to /usr/bin after release upgrade2023-11-27T00:00:00+01:002023-11-27T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-11-27:/2023/11/27/manually-move-bin-to-usrbin-after-release-upgrade/<p>The problem is that <code>do-release-upgrade</code> from Ubuntu 18.04 LTS to Ubuntu 20.04 LTS does …</p><p>The problem is that <code>do-release-upgrade</code> from Ubuntu 18.04 LTS to Ubuntu 20.04 LTS does not do the move.</p>
<p>Fresh installations however get installed with the <code>/usr/bin</code> move already done.</p>
<p>So a shell script with the interpreter <code>#!/usr/bin/bash</code> will break.</p>
<h1>Preparations</h1>
<p>First we must ensure that both <code>/bin</code> and <code>/usr/bin</code> are in the path</p>
<div class="highlight"><pre><span></span><code>root@staging:~# <span class="nb">echo</span> <span class="nv">$PATH</span>
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/lib/npm/bin
</code></pre></div>
<h1>Do the move as root</h1>
<p>Doing this migration on a production system should be save, in any case you must do a backup first and test said backup just in case of an emergency!</p>
<p>Now execute these steps and verify with the <code>ls</code> command at the end:</p>
<div class="highlight"><pre><span></span><code>root@staging:~# <span class="nb">cd</span> /
root@staging:/# mv bin/* usr/bin/
root@staging:/# rmdir bin
root@staging:/# ln -s usr/bin bin
root@staging:/# ls -asl /bin
<span class="m">0</span> lrwxrwxrwx <span class="m">1</span> root root <span class="m">7</span> Nov <span class="m">15</span> <span class="m">17</span>:25 /bin -> usr/bin
</code></pre></div>
<p>Did you succeed? Consider donating to my blog 😃</p>Debian 12 with KDE or Kubuntu 22.04 LTS Full Disk Encryption with LUKS2 and Windows 11 Dual-Boot2023-10-20T00:00:00+02:002023-10-20T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-10-20:/2023/10/20/debian-12-with-kde-or-kubuntu-2204-lts-full-disk-encryption-with-luks2-and-windows-11-dual-boot/<p>I assume you already have a USB drive with the regular <a href="https://www.debian.org/">Debian 12</a> or <a href="https://kubuntu.org/getkubuntu/">Kubuntu</a> image …</p><p>I assume you already have a USB drive with the regular <a href="https://www.debian.org/">Debian 12</a> or <a href="https://kubuntu.org/getkubuntu/">Kubuntu</a> image flashed directly on it. Having it as a file on the drive will most probably not work unless you have a special setup on that stick.</p>
<p>If not, you can do this step from the existing Windows 11 installation with a tool like <a href="https://rufus.ie/">Rufus</a>.</p>
<h1>Shrink the Windows C:\ drive</h1>
<p>On my machine the internal drive is 1TB in size.
Windows is known for its hunger for space, so I reduced it to 220GB.</p>
<p>In the start menu type "Computer Management" and launch as Administrator.
Then under the "Disk Management" find the <code>C</code> drive and shrink it so that it remains roughly 220GB (or more depending on your needs) in size and hit apply.</p>
<p>Doing this on an alreay used drive takes more time than on a fresh one so grab a tea.</p>
<h2>Install the latest firmware</h2>
<p>Especially the BIOS, TouchPad, Chipset and the WiFi/WLan adapter firmware updates contain critical fixes.
Some are security relevant, others just prevent crashes.</p>
<p>Some (Samsung) SSDs need firmware updates too so they don't burn out, best to install all the updates.</p>
<h1>Prepare the BIOS/UEFI</h1>
<p>First we must enable the "3rd party CA" or "partner keys" in the UEFI so we can boot from the USB stick that holds the normal image.</p>
<p>Alternatively, one could also either add the Linux Boot Keys specifically or simply disable Secure Boot.</p>
<h2>Video Memory</h2>
<p>With integrated AMD or Intel graphics the BIOS usually has an option for the initial VRAM split.
On my AMD system increasing the default value from 128MB to 2GB makes animations smooth and allows me to play games.</p>
<p>My Intel machine has a maximum of 256MB, the animations are smooth too so I assume they make that adjustment in the driver at runtime.</p>
<h1>Boot in "Try Kubuntu" mode</h1>
<p>This allows us to execute any program like the shell that we need.</p>
<p>Start the setup and set your favorite keyboard layout and timezone.
Connect to cable ethernet or WiFi to fetch updates automatically during the installation later and lookup things like this guide.</p>
<p>When the setup asks you to reformat the whole disk, go to the next step.</p>
<h2>Partitioning</h2>
<p>From the start menu, open <code>partitionmanager</code> and add a 2GB partition formatted with <code>ext2</code>.</p>
<p>Then add a second partition that uses all the space and format it with <code>lvm2 pv</code> and check the <code>enable encryption</code> checkbox, call it <code>crypto-root</code> or something not too generic.</p>
<p>Enter your disk encryption password here.</p>
<p><strong>Important:</strong> if you forget that password all your data is lost forever.</p>
<p><strong>Important 2:</strong> if you share said password all your data is potentially compromised.</p>
<p>If everything looks good hit <code>apply</code> and take a sip of tea.</p>
<p>Open the encrypted container if it is not open already.</p>
<h3>Encrypted partitions / logical volumes</h3>
<p>First, open a Terminal and type <code>sudo -i</code> (and press Enter to execute) to become root.
Now you don't have any restrictions and can do a lot of good as well as destroy your whole machine. ⚖️</p>
<p>First we need to add a volume group consisting of the space we just allocated that we name <code>crypto-vg</code> or something:</p>
<div class="highlight"><pre><span></span><code>vgcreate crypto-vg /dev/mapper/crypto-root
</code></pre></div>
<p>Use the <code>vgs</code> command or <code>vgdisplay</code> to confirm the size.</p>
<h3>Logical partitions</h3>
<p>Now it is time to add a SWAP partition. This one should be at least the size of your RAM for suspand-to-disk aka. hybernation up to double that.
I choose double the memory because I might upgrade my RAM in a year or two and I don't want to repartition then:</p>
<div class="highlight"><pre><span></span><code>lvcreate --size 64GB crypto-vg
</code></pre></div>
<p>Finally we create the logical partition that will hold our OS and data:</p>
<div class="highlight"><pre><span></span><code>lvcreate --extends <span class="m">100</span>%FREE crypto-vg
</code></pre></div>
<p>Note that we did not format any partition yet, the installer will do that for us.</p>
<h2>Installing the operating system</h2>
<p>Back in the installer we choose "custom partitioning" and click next.</p>
<p>In the next dialogue we should find all the physical and logical partitions:</p>
<ul>
<li>Ignore the Windows or Bitlocker as well as the EFI and Recovery partitions</li>
<li>Pick the 2GB ext2 partition and use it as <code>/boot</code></li>
<li>Select the 64GB partition with the long name as <code>SWAP</code> with the <code>SWAP</code> filesystem</li>
<li>Select the huge partition with the long name as <code>/</code> with the <code>btrfs</code> filesystem</li>
</ul>
<p>Let the installer reformat the 2GB <code>/boot</code> partition if it asks.</p>
<p>Now continue with the normal setup.</p>
<p>When it starts to install software or updates change back to the terminal and continue while it runs in the background.</p>
<h2>Configuring the unlock screen</h2>
<p>Without this screen the system will boot half and then be stuck.
It is absolutely possible to contine the boot by manually unlocking the partition, but is clumbsy and we would have to type the <code>cryptsetup</code> command everytime.</p>
<p>Instead we are storing the UUID of our container in a special file: <code>/target/etc/crypttab</code>.
This file has the following format:</p>
<div class="highlight"><pre><span></span><code>label UUID=... none luks,discard
</code></pre></div>
<p>Now we need the actual UUID to replace the <code>...</code> that we can get from this command:</p>
<div class="highlight"><pre><span></span><code>blkid <span class="p">|</span> grep LUKS
</code></pre></div>
<p>Copy the parts between <code>UUID="</code> and <code>"</code> withouth the <code>"</code> and we are done.
In the end the line you add should look like this, where <code>none</code> means that the boot loader should ask for your password:</p>
<div class="highlight"><pre><span></span><code>kryptoWurzel UUID=abcdef01-2345-6789-abcd-ef0123456789 none discard,luks
</code></pre></div>
<p><code>discard</code> is for SSDs and <code>luks</code> because we use a LUKSv2 container.</p>
<h3>Updating the initramfs aka. boot system</h3>
<p>Sadly, the installer is not considering our shiny new crypttab file and we have to a final touch.</p>
<p>First we have to verify that the <code>/proc</code> filesystem is still mounted in the <code>/target/proc</code>.
If not, bind mount it like this:</p>
<div class="highlight"><pre><span></span><code>mount --bind /proc /target/proc
</code></pre></div>
<p>When the installer is on the last page, go back to the terminal and type this:</p>
<div class="highlight"><pre><span></span><code>chroot /target /bin/bash
</code></pre></div>
<p>You are now inside the new installation and everything should be mounted correctly already.
Now we can regenerate the initramfs with this command:</p>
<div class="highlight"><pre><span></span><code>update-initramfs -c -k all
</code></pre></div>
<p>Once that is through type <code>exit</code> + Enter to close each layer in the terminal.</p>
<p>Now we tell the installer to reboot the system.</p>
<p>After the BIOS splash screen we should be greeted by GRUB that allows us to choose between Kubuntu Linux and Windows 11.</p>
<p>And finally the unlock screen for the full disk encryption (FDE) is the confirmation that everything worked.
After entering your personal password the system behaves normally and everything works just like always but is more secure than without the encryption.</p>
<p>Congratulations and enjoy your tea.</p>What is secure by default in docker swarm?2023-10-01T00:00:00+02:002023-10-01T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-10-01:/2023/10/1/what-is-secure-by-default-in-docker-swarm/<p>Docker Swarm has many cool features like load-balancing with an overlay network and puts special emphasis …</p><p>Docker Swarm has many cool features like load-balancing with an overlay network and puts special emphasis on this one:</p>
<blockquote>
<ul>
<li><strong>Secure by default:</strong> Each node in the swarm enforces TLS mutual authentication and encryption to secure communications between itself and all other nodes.</li>
</ul>
<p>Source: <a href="https://docs.docker.com/engine/swarm/">https://docs.docker.com/engine/swarm/</a></p>
</blockquote>
<p>Well what does that mean specifically?</p>
<blockquote>
<p>All swarm service management traffic is encrypted by default, using the AES algorithm in GCM mode. Manager nodes in the swarm rotate the key used to encrypt gossip data every 12 hours.</p>
<p>Source: <a href="https://docs.docker.com/network/drivers/overlay/">https://docs.docker.com/network/drivers/overlay/</a></p>
</blockquote>
<p>That leaves the overlay networks <strong>un-encrypted</strong>.</p>
<p>There is an option for networks <code>--opt encrypted</code> that enables IPSec for application data but docker does not recommend it:</p>
<blockquote>
<p>To encrypt application data as well, add --opt encrypted when creating the overlay network. This enables IPSEC encryption at the level of the vxlan. This encryption imposes a non-negligible performance penalty, so you should test this option before using it in production.</p>
<p>Source: <a href="https://docs.docker.com/network/drivers/overlay/#encrypt-traffic-on-an-overlay-network">https://docs.docker.com/network/drivers/overlay/#encrypt-traffic-on-an-overlay-network</a></p>
</blockquote>
<h1>What does it look like on the network?</h1>
<p>Observing a browser like Firefox that has connected to one machine in the swarm who's data gets proxied to the machine actually running the container:</p>
<p><img alt="Unencrypted WebSocket in Wireshark" src="https://estada.ch/2023/10/1/what-is-secure-by-default-in-docker-swarm/2023-10-01_docker-swarm-secure-by-default/Unencrypted_WebSocket.png"></p>
<p>In this example we can see the JSON in the WebSocket connection in plain text.</p>
<h1>Encrypting machine to machine traffic with Wireguard</h1>
<p><a href="https://www.wireguard.com/">Wireguard</a> has a very low performance impact and can run alongside a production, high performance cluster that is inside one datacenter or distributed over multiple datacenters.</p>
<p>It is important that we:</p>
<ul>
<li>do not use overlapping ranges, docker uses anything from <code>10.0.0.0/8</code>, <code>172.17.0.0/16</code>, and <code>172.18.0.0/16</code> by default</li>
<li>use private ranges inside Wireguard so we notice broken routes</li>
<li>have direct connections between all nodes in case one goes down</li>
</ul>
<p>This can be generated with something like ansible so that there is one list of all the public and private keys and then using a template generating the specific configurations for all servers.</p>ÖBB unter Wasser2023-08-28T00:00:00+02:002023-08-28T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-08-28:/2023/8/28/obb-unter-wasser/de/<p>Heute wurde ein Teil der Bahn-Infrastruktur in Österreich und Bayern überschwemmt.
Am Morgen fing es mit …</p><p>Heute wurde ein Teil der Bahn-Infrastruktur in Österreich und Bayern überschwemmt.
Am Morgen fing es mit einem kleinen Unterbruch zwischen Ötztal und Landeck-Zams, aber kontinuierlicher Regen hat weitere Bereiche ausgeschaltet.</p>
<p>Aktuell sind folgende Strecken unterbrochen:</p>
<ul>
<li>Schönwies- Imst Pitztal</li>
<li>Innsbruck Hbf</li>
<li>Bruck Fusch- Taxenbach Rauris</li>
<li>Golling-Abtenau-Werfen</li>
</ul>
<p>Dazu kommen noch diese Einschränkungen</p>
<ul>
<li>Rosenheim nur Einspurig</li>
<li>St. Johann Bauarbeiten</li>
</ul>
<p><img alt="Karte 18:04:03" src="https://estada.ch/2023/8/28/obb-unter-wasser/de/2023-08-28_oebb-unter-wasser/Karte-18.04.03.png"></p>
<h1>Ausweichen über Zell am See?</h1>
<p>Da das Deutsche Eck noch immer von dem <a href="https://orf.at/stories/3327548/">Vorfall mit der brennenden Lok</a> eingeschränkt ist wurden bereits Züge über Zell am See umgeleitet.
Mit den aktuellen Unwetter ist jetzt diese Ausweichroute auch ausgefallen.</p>
<p>Damit ist aktuell kein Zug Verkehr mehr zwischen Salzburg und der Schweiz möglich.</p>
<h1>Prognosen</h1>
<p>Laut den ÖBB werden Teile der Strecke morgen dem 29-sten wieder geöffnet oder mit Schienenersatz benutzbar.</p>
<p>Letztes Jahr trat die Salzach schonmal nach starken Regenfällen über die Ufer, das ist heute nochnicht passiert, wird aber vermutlich in den nächsten Tagen geschehen.</p>
<p>Darum vermute ich wird die Zugstrecke von Salzburg nach Zell am See nicht so schnell wieder benutzbar werden.
Damit bliebe für den Moment nur der Regio/Meridian nach München und dann mit dem ICE nach Zürich.</p>Continous Integration with Rust and GitLab Self-Hosting2023-07-31T00:00:00+02:002023-07-31T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-07-31:/2023/7/31/continous-integration-with-rust-and-gitlab-self-hosting/<p><a href="https://youtu.be/TrPyUVundbM"><img alt="Continous Integration with Rust and GitLab Self-Hosting Video Thumbnail" src="https://estada.ch/2023/7/31/continous-integration-with-rust-and-gitlab-self-hosting/2023-07-31_ci-with-rust-and-gitlab/CI_with_Rust_Teaser.webp"></a></p>
<p>I gave a talk at <a href="https://rust-linz.at/">Rust Linz</a> about how to speedup your CI builds with stable …</p><p><a href="https://youtu.be/TrPyUVundbM"><img alt="Continous Integration with Rust and GitLab Self-Hosting Video Thumbnail" src="https://estada.ch/2023/7/31/continous-integration-with-rust-and-gitlab-self-hosting/2023-07-31_ci-with-rust-and-gitlab/CI_with_Rust_Teaser.webp"></a></p>
<p>I gave a talk at <a href="https://rust-linz.at/">Rust Linz</a> about how to speedup your CI builds with stable rust in the gitlab pipeline.
Most of the code can be applied to other build systems as well.</p>
<p>I presented on my Steam Deck. Sadly there were some issues with the sound and speaker cam resulting in a lower than usual quality of the recording.
Enjoy!</p>Can AI draw train tracks and a station in the mountains?2023-07-23T00:00:00+02:002023-07-23T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-07-23:/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/<p>The big hidden question here is:
Do today's LLM's know that trains and tracks are not …</p><p>The big hidden question here is:
Do today's LLM's know that trains and tracks are not just common together but that one usually needs the other?</p>
<h1>make me an image of an öbb train station in the alps</h1>
<p>The third building has some O and some B's in it, nice 👍</p>
<div class="gallery">
<p><img alt="01" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/01.jpg">
<img alt="02" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/02.jpg">
<img alt="03" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/03.jpg">
<img alt="04" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/04.jpg"></p>
</div>
<h1>base it on the station Selzthal in the spring</h1>
<p>This time the bot even responds with text and a map first:</p>
<blockquote>
<p>Okay, I’ll try to base it on the station Selzthal in the spring. Selzthal is a railway station in Austria that is a rail hub for East-West and North-South connections1. It is located in the district of Liezen in Styria1.</p>
</blockquote>
<p>Hm, the third picture has something off, can you spot it?</p>
<div class="gallery">
<p><img alt="05" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/05.jpg">
<img alt="06" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/06.jpg">
<img alt="07" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/07.jpg">
<img alt="08" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/08.jpg"></p>
</div>
<h1>add more tracks as well as a pass through line, add a cargo train in the background</h1>
<p>This could not have gone any better ... </p>
<div class="gallery">
<p><img alt="09" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/09.jpg">
<img alt="10" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/10.jpg">
<img alt="11" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/11.jpg">
<img alt="12" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/12.jpg"></p>
</div>
<h1>Use lighting like on a post card</h1>
<p>Trains run on tracks, well sometimes they don't 🫠</p>
<div class="gallery">
<p><img alt="13" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/13.jpg">
<img alt="14" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/14.jpg">
<img alt="15" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/15.jpg">
<img alt="16" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/16.jpg"></p>
</div>
<h1>focus on the tracks and make them fully connected</h1>
<p>Connected is such a hard word. Let's just hope no-one tries to connect other kinds of data with an AI... 🤫</p>
<div class="gallery">
<p><img alt="17" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/17.jpg">
<img alt="18" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/18.jpg">
<img alt="19" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/19.jpg">
<img alt="20" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/20.jpg"></p>
</div>
<h1>Draw less forks, make one line taper off to the left, another line should follow the rolly hills on the right</h1>
<p>Maybe there were too many lines already, but this time it actually managed to get the "more tracks" out of the generator prompt, but not the actual picture.</p>
<div class="gallery">
<p><img alt="21" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/21.jpg">
<img alt="22" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/22.jpg">
<img alt="23" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/23.jpg">
<img alt="24" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/25.jpg"></p>
</div>
<h1>Make the picture more photo realistic</h1>
<p>More, less who is to say what words mean to a large language model... 🫣</p>
<p>Atleast the forth picture looks very nice.</p>
<div class="gallery">
<p><img alt="25" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/25.jpg">
<img alt="26" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/26.jpg">
<img alt="27" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/27.jpg">
<img alt="28" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/28.jpg"></p>
</div>
<h1>use an uninterrupted, double pencil stroke as the base of each rail track</h1>
<p>It added the word "style" to the prompt so naturally instead of making the tracks out of lines it drew the whole thing like a pencil 🙈</p>
<div class="gallery">
<p><img alt="29" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/29.jpg">
<img alt="30" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/30.jpg">
<img alt="31" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/31.jpg">
<img alt="32" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/32.jpg"></p>
</div>
<h1>Can you add a train station cafe?</h1>
<p>It responds with a nice text first and the listed references actually point to stuff about cafe's in trainstations:</p>
<blockquote>
<p>Sure, I can add a train station cafe. A train station cafe is a place where you can enjoy some food and drinks while waiting for your train or after your journey. Some train station cafes are located inside the station building, while others are nearby or on the platform. Some examples of train station cafes are To Treno Sto Rouf in Athens, Greece1, Brauhaus 1516 in Augsburg, Germany, and The Train Station Cafe in Anniston, Alabama, USA.</p>
</blockquote>
<p>Well the pictures don't have any cafes in them so: Go home, you are drunk!</p>
<div class="gallery">
<p><img alt="33" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/33.jpg">
<img alt="34" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/34.jpg">
<img alt="35" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/35.jpg">
<img alt="36" src="https://estada.ch/2023/7/23/can-ai-draw-train-tracks-and-a-station-in-the-mountains/2023-07-23_image-generator-vs-train-tracks/36.jpg"></p>
</div>Trigger Plesk Webhook Handler with Curl or Wget and an OAuth2 Token and a private Gitlab Repo2023-06-24T00:00:00+02:002023-06-24T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-06-24:/2023/6/24/trigger-plesk-webhook-handler-with-curl-or-wget-and-an-oauth2-token-and-a-private-gitlab-repo/<p>When deploying a small PHP application on a shared hoster that uses plesk, I recommend using …</p><p>When deploying a small PHP application on a shared hoster that uses plesk, I recommend using git.</p>
<p>Then the final deployment workflow goes like this:</p>
<ol>
<li>A developer sends commits with <code>git push</code> to the gitlab instance</li>
<li>The gitlab instance then sends a <code>POST</code> request to the Plesk instance</li>
<li>The Plesk instance now starts pulling the repo onto a machine</li>
<li>If auto deploy is activated Plesk will then unpack the contents into the webspace</li>
</ol>
<h1>Setup oauth 2.0 token on Gitlab</h1>
<p>Generating the token in Gitlab is very clean. Go to <code>Settings</code> -> <code>Access Tokens</code> and create it with the <code>Developer</code>role as well as the <code>read_api</code> and <code>read_repository</code> permissions.
This is also the time to set yourself a reminder for next year when the token will expire, sadly there are no infinite tokens anymore.</p>
<p>What is missing is the docs on what user you have to use when using the token with HTTP Basic Auth instead of a HTTP header.</p>
<p>The answer is: <strong>oauth2</strong></p>
<h1>Setup the Domain & Git Deploy</h1>
<p>Follow the normal workflow in Plesk to add a domain, let's go with <code>example.org</code>.</p>
<p>Then under <code>Git</code> paste the HTTPS Clone Url of your private repo and add the HTTP Basic Auth params:</p>
<div class="highlight"><pre><span></span><code><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="nl">oauth2</span><span class="p">:</span><span class="err">{</span><span class="n">enter</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">token</span><span class="w"> </span><span class="n">here</span><span class="err">}</span><span class="nv">@gitlab</span><span class="p">.</span><span class="n">instance</span><span class="p">.</span><span class="n">example</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="k">group</span><span class="o">/</span><span class="n">subgroup</span><span class="o">/</span><span class="n">repo</span><span class="p">.</span><span class="n">git</span><span class="w"></span>
</code></pre></div>
<p>This step will let you test the token until it works.</p>
<p>Now Plesk pulled the repo you then select the branch and save the settings.</p>
<h1>Getting the Webhook</h1>
<p>First, go back out to the <code>Websites & Domains</code> in Plesk.
Second, go back into the domain settings and find the <code>Git</code> deploy.</p>
<p>Now there is a new icon in the bottom that looks like three sliders or a little cog wheel (depending on your version) that opens the advanced settings.</p>
<p>Now in there is the actual hook URI that looks something like this, with the management port for example <code>8443</code>:</p>
<div class="highlight"><pre><span></span><code>https://server.hoster.net:8443/modules/git/public/web-hook.php?uuid=abcdef-abcdef-abcdef-abcdef-abcdef
</code></pre></div>
<p>You can test it with a command like this:</p>
<div class="highlight"><pre><span></span><code>curl -x POST <span class="cp">${</span><span class="n">HOOK_URI</span><span class="cp">}</span>
</code></pre></div>
<h2>Updating the oauth2 token next year</h2>
<p>In this menu change the password to the new token you will generate next year.</p>
<h1>Adding the Hook to Gitlab</h1>
<p>Now add the hook to the repo under <code>Settings</code> -> <code>Webhooks</code>.</p>
<p>After pasting the <code>HOOK_URI</code> select <code>Push events</code> and then <code>All branches</code>, you can be more specific for bigger repos.
On the bottom enable <code>SSL verification</code>, test and then save.</p>
<h2>Alternative integrate with CI</h2>
<p>Make sure you have <code>wget</code> or <code>curl</code> then execute a POST request to the <code>HOOK_URI</code>.
It looks like because Plesk does not parse the contents of the request it can be empty as well.</p>
<p>I recommend going with the hook variant as it is faster and does not require any runners unless you are already running a job for other checks, lints or tests.</p>Artists will be safe, playing with bing AI and DALL-E2023-06-23T00:00:00+02:002023-06-23T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-06-23:/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/<p>Letting computers generate content with linear math and random noise is very cool right now.
Let's …</p><p>Letting computers generate content with linear math and random noise is very cool right now.
Let's have a look how reliable this is in the space where humans are flexible with exact semantics: Images</p>
<p>I used prompts that build upon each other, so the results are compounding.
You can see the resulting prompt in the headings.</p>
<p>Now it is time for some fun and no horror at all 😉</p>
<h1>a picture of an office with personnel and a funny text</h1>
<p>Wow, what a horror show, and for some reason only one had "text".
Cursed and straight from hell.</p>
<div class="gallery">
<p><img alt="01" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/01_a-picture-of-an-office-with-personnel-and-a-funny-text.jpg">
<img alt="02" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/02_a-picture-of-an-office-with-personnel-and-a-funny-text.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background</h1>
<p>Okay, more text and mostly human looking people. I wonder who beat up the three people on the outside ... And what happend to the fingers of the guy posing like he had a telescope? 😦</p>
<div class="gallery">
<p><img alt="03" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/03_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background.jpg">
<img alt="04" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/04_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background.jpg">
<img alt="05" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/05_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975</h1>
<p>Hm, I guess I misspelled that fashion label. But why are all the people made out of clay now? 🤣</p>
<div class="gallery">
<p><img alt="06" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/06_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975.jpg">
<img alt="07" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/07_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975.jpg">
<img alt="08" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/08_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975.jpg">
<img alt="09" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/09_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975 and a cute dog</h1>
<p>I need more cuteness, robot: add me a dog!</p>
<p>Oh, noo 😯 ... I guess it got my typo label on one of the signes, I mean sometimes.</p>
<div class="gallery">
<p><img alt="10" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/10_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog.jpg">
<img alt="11" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/11_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog.jpg">
<img alt="12" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/12_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog.jpg">
<img alt="13" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/13_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975 and a cute dog with realistic faces like the tv series vikings</h1>
<p>I know, just tell it to pay more attention to the faces.</p>
<p>Well that backfired, look at the dog in the first image 🙀</p>
<p>"anatomically correct" is especially apparent in the last image where we can spot the "proud Strudel folks" 🙃
However, where is the influence of this drama show with beautiful people in the last images?</p>
<div class="gallery">
<p><img alt="14" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/14_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="15" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/15_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="16" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/16_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="17" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/17_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li-1.jpg">
<img alt="18" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/17_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="19" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/19_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="20" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/20_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg">
<img alt="21" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/21_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-in-the-style-of-a-ballinicage-model-show-form-1975-and-a-cute-dog-with-realistic-faces-li.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975 and a cute dog with realistic faces like the tv series vikings and anatomically correct and a big window in the background with a view of a forest inside a valley</h1>
<p>Let's fix that by adding forests since that is closer to the themes in "the source material" 🤫</p>
<p>Now we have entered the poster space. Some furry coats are a nice touch.</p>
<p>Do I see a Bob Ross like character in there? 🫣</p>
<div class="gallery">
<p><img alt="22" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/22_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-dog-with-realistic-faces-like-the-tv-series-vikings-and-anatomically-correct-and-a-big-wi.jpg">
<img alt="23" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/23_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-dog-with-realistic-faces-like-the-tv-series-vikings-and-anatomically-correct-and-a-big-wi.jpg">
<img alt="24" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/24_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-dog-with-realistic-faces-like-the-tv-series-vikings-and-anatomically-correct-and-a-big-wi.jpg">
<img alt="25" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/25_a-group-of-people-in-an-office-with-a-motivational-sign-in-the-background-dog-with-realistic-faces-like-the-tv-series-vikings-and-anatomically-correct-and-a-big-wi.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975 and a cute dog with realistic faces like the tv series vikings and anatomically correct and a big window in the background with a view of a forest inside a valley with more natural light and placed in a fiord</h1>
<p>Wow this prompt is getting long. I basically added: <code>more natural light and placed in a fiord</code></p>
<p>Now the guy on the left actually looks like a human again, the dogs are freakish 🫠</p>
<p>And who is sitting on the right in the last picture? This gives me an idea 😇</p>
<div class="gallery">
<p><img alt="26_fiord" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/26_fiord.jpg">
<img alt="27_fiord" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/27_fiord.jpg">
<img alt="28_fiord" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/28_fiord.jpg">
<img alt="29_fiord" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/29_fiord.jpg"></p>
</div>
<h1>a group of people in an office with a motivational sign in the background in the style of a ballinicage model show form 1975 and a cute dog with realistic faces like the tv series vikings and anatomically correct and a big window in the background with a view of a forest inside a valley with more natural light and placed in a fiord with krampus and other demons</h1>
<p>This is surprisingly good. Letting the linear math algorithm hallucinate monsters gets most of the human characters back into a "could be alive" shape.</p>
<p>I am sure my next prompt will make that even better. I mean look at the first picture, is that not just like your office in the alps? 😂</p>
<div class="gallery">
<p><img alt="30_krampus" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/30_krampus-and-other-demons.jpg">
<img alt="31_krampus" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/31_krampus-and-other-demons.jpg">
<img alt="32_krampus" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/32_krampus-and-other-demons.jpg">
<img alt="33_krampus" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/33_krampus-and-other-demons.jpg"></p>
</div>
<h1>... with humans models and the demons have a dark-colored aura</h1>
<p>I may have over cooked it, but look at the cute, fluffy, white deer with the red horns 🤩</p>
<div class="gallery">
<p><img alt="34_dark" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/34_dark-colored-aura.jpg">
<img alt="35_dark" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/35_dark-colored-aura.jpg">
<img alt="36_dark" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/36_dark-colored-aura.jpg">
<img alt="37_dark" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/37_dark-colored-aura.jpg"></p>
</div>
<h1>... make the management pandemonium</h1>
<p>This answers the question: "Oh, you had a bad dream?" 🎃</p>
<p>Have you noticed that the dog now is a bore or a sheep? I guess they must transform when the move to the "beautiful farm in the country side far far away where you can never visit" </p>
<div class="gallery">
<p><img alt="38_pandemonium" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/38_pandemonium.jpg">
<img alt="39_pandemonium" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/39_pandemonium.jpg">
<img alt="40_pandemonium" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/40_pandemonium.jpg">
<img alt="41_pandemonium" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/41_pandemonium.jpg"></p>
</div>
<h1>Time for some protection 🪬</h1>
<p><img alt="integrate-a-hamsa" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/integrate-a-hamsa.webp"></p>
<p><span class="inverted-daemonic">I fear the worst already 😶🌫️ ...</span></p>
<p>Oh, who is that lovely fellow in picture number 4 glancing into the office?</p>
<div class="gallery">
<p><img alt="42_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/42_protection.jpg">
<img alt="43_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/43_protection.jpg">
<img alt="44_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/44_protection.jpg">
<img alt="45_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/45_protection.jpg">
<img alt="46_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/46_protection.jpg">
<img alt="47_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/47_protection.jpg">
<img alt="48_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/48_protection.jpg">
<img alt="49_protection" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/49_protection.jpg"></p>
</div>
<h1>... with a flying raven</h1>
<p>Okay, I guess we have peeked ...
The fluffy monster in the last image says it all with it's look:</p>
<p>Enough internet for today!</p>
<div class="gallery">
<p><img alt="50_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/50_raven.jpg">
<img alt="51_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/51_raven.jpg">
<img alt="52_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/52_raven.jpg">
<img alt="53_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/53_raven.jpg">
<img alt="54_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/54_raven.jpg">
<img alt="55_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/55_raven.jpg">
<img alt="56_raven" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/56_raven.jpg"></p>
</div>
<h1>a bored fluffy daemon in the center on a stone table</h1>
<p>Well the table is not quite stone and suddenly all deamons are fluffy instead of just the one 🙈🙉🙊</p>
<p><span class="inverted-daemonic">And with the 3 point lighting I wish you a great weekend 🔥</span> and remember, that using auto complete and spell checking did not ruin society but poverty and hunger are.</p>
<div class="gallery">
<p><img alt="57_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/57_bored.jpg">
<img alt="58_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/58_bored.jpg">
<img alt="59_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/59_bored.jpg">
<img alt="60_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/60_bored.jpg">
<img alt="61_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/61_bored.jpg">
<img alt="62_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/62_bored.jpg">
<img alt="63_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/63_bored.jpg">
<img alt="64_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/64_bored.jpg">
<img alt="65_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/65_bored.jpg">
<img alt="66_bored" src="https://estada.ch/2023/6/23/artists-will-be-safe-playing-with-bing-ai-and-dall-e/2023-06-23_playing-with-image-generators/66_bored.jpg"></p>
</div>Recordings of the March and April Rust Zürisee meetups2023-04-22T00:00:00+02:002023-04-22T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-04-22:/2023/4/22/recordings-of-the-march-and-april-rust-zurisee-meetups/<p>We are looking for speakers and hosts for future events around the lake of Zürich.</p>
<ul>
<li>Join …</li></ul><p>We are looking for speakers and hosts for future events around the lake of Zürich.</p>
<ul>
<li>Join our matrix room: <a href="https://matrix.to/#/#rust-zuerisee:matrix.coredump.ch">#rust-zuerisee:matrix.coredump.ch</a></li>
<li>Come to our events: <a href="https://www.meetup.com/de-DE/rust-zurich/">https://www.meetup.com/de-DE/rust-zurich/</a></li>
</ul>
<h1>Mike Kefeder: Introduction to Rust on bare metal</h1>
<p>Mike gave a wonderful talk and helped a lot with the recordings, thank you!</p>
<p><a href="https://youtu.be/KECu_piSM5s"><img alt="Thumbnail Mike" src="https://estada.ch/2023/4/22/recordings-of-the-march-and-april-rust-zurisee-meetups/2023-04-22_rust-zurisee-videos/Thumbnail_Mike.png"></a></p>
<h1>Adrian Alic: Writing Performant Concurrent Data Structures</h1>
<p>Adrian made a lot of measurements that you should checkout.</p>
<p><a href="https://youtu.be/XKODaZgKcnE"><img alt="Thumbnail Adrian" src="https://estada.ch/2023/4/22/recordings-of-the-march-and-april-rust-zurisee-meetups/2023-04-22_rust-zurisee-videos/Thumbnail_Adrian.png"></a></p>
<h1>Jaroslaw Surkont & Christian Ribeaud: Sett - data encryption and transfer made easy(ier)</h1>
<p>Christian & Jaroslaw told us about their journey from python to rust.</p>
<p>Their recording will be released on the 27th, stay tuned.</p>
<p><a href="https://youtu.be/cP7uy97uU4A"><img alt="Thumbnail sett" src="https://estada.ch/2023/4/22/recordings-of-the-march-and-april-rust-zurisee-meetups/2023-04-22_rust-zurisee-videos/Thumbnail_sett.png"></a></p>Uninstall cups from Ubuntu server VM and gain 137MB of memory2023-04-11T00:00:00+02:002023-04-11T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-04-11:/2023/4/11/uninstall-cups-from-ubuntu-server-vm-and-gain-137mb-of-memory/<p>Today, one of the ubuntu 20.04 servers was swapping a crazy amount of RAM to …</p><p>Today, one of the ubuntu 20.04 servers was swapping a crazy amount of RAM to disk that caused the load factor to rise from the usual ~0.4 to over 25.
The machine in question should have been running exactly three services.
All of them in-house products and optimised to run on the specific size of theses VMs.</p>
<p>To my surprise there was a <code>cups-browsed</code> running inside a snap package.
Why was it installed on a LTS server?
Sadly, the snap history that <code>snap changes</code> returns contains just the last 24h.</p>
<p>So the only other hint was in <code>journalctl</code> where the entries from <code>storehelpers.go</code> report errors while looking for updates.</p>
<p>Let's get on with the fix:</p>
<div class="highlight"><pre><span></span><code>snap remove cups
</code></pre></div>
<h1>Why is this a problem?</h1>
<h2>The resources</h2>
<p>On a tiny VM with 1024MB of RAM 137MB is roughly 13% of the available memory.
And the disk space is at-least 56MB according to <code>du -hc /var/lib/snapd/snaps/*</code></p>
<p>If the cloud provider had added it to the base image, then they just waste disk space and RAM for their clients the question is do they try to up-sell you?</p>
<p>If it is part of the Ubuntu server default image this must have slipped trough the cracks and should be fixed.</p>
<h2>The attack surface</h2>
<p>Any piece of software that is not needed is a potential security problem.
<code>cups-browsed</code> is listening to broadcast mDNS requests (and maybe sending them out as well?) in the data center, so not great for a quiet operation.</p>Encrypted Filesystem Storage: Master Thesis Video2023-03-11T00:00:00+01:002023-03-11T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-03-11:/2023/3/11/encrypted-filesystem-storage-master-thesis-video/<p><a href="https://youtu.be/Lyv6ybfOSjs"><img alt="Encrypted Filesystem Storage Video Thumbnail" src="https://estada.ch/2023/3/11/encrypted-filesystem-storage-master-thesis-video/2023-03-11_encrypted-filesystem-storage-part1/Thumbnail.png"></a></p>
<p>I am looking for a rust job currently.
Please contact me via the social media channels …</p><p><a href="https://youtu.be/Lyv6ybfOSjs"><img alt="Encrypted Filesystem Storage Video Thumbnail" src="https://estada.ch/2023/3/11/encrypted-filesystem-storage-master-thesis-video/2023-03-11_encrypted-filesystem-storage-part1/Thumbnail.png"></a></p>
<p>I am looking for a rust job currently.
Please contact me via the social media channels linked below or send an email to my-name@this-domain.</p>
<p>I gave this talk in preparation for my defense, so it is a little longer than the actual defense.</p>
<p><a href="https://estada.ch/2023/3/11/encrypted-filesystem-storage-master-thesis-video/2023-03-11_encrypted-filesystem-storage-part1/encrypted-filesystem-for-applications.pdf"><img alt="Download Slides" src="/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>Telegram (and some others) have a weird TCP timeout during the TLS handshake over IPv62023-02-06T00:00:00+01:002023-02-06T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-02-06:/2023/2/6/telegram-and-some-others-have-a-weird-tcp-timeout-during-the-tls-handshake-over-ipv6/<p>For the last two months telegram and some others have showed a weird behaviour:</p>
<ol>
<li>DNS responds …</li></ol><p>For the last two months telegram and some others have showed a weird behaviour:</p>
<ol>
<li>DNS responds with AAAA and A records</li>
<li>Establish a TCP connection to port <code>80</code> over IPv6<ol>
<li>Send <code>HTTP GET /</code> get the HTTPS redirect</li>
</ol>
</li>
<li>Establish a new TCP connection to port <code>443</code><ol>
<li>Send <code>TLSv1.3 handshake, Client hello</code></li>
<li>... and wait for the timeout</li>
</ol>
</li>
</ol>
<p>Here is a full log:</p>
<div class="highlight"><pre><span></span><code>curl https://web.telegram.org -v
* Trying <span class="o">[</span><span class="m">2001</span>:67c:4e8:f004::9<span class="o">]</span>:443...
* Connected to web.telegram.org <span class="o">(</span><span class="m">2001</span>:67c:4e8:f004::9<span class="o">)</span> port <span class="m">443</span> <span class="o">(</span><span class="c1">#0)</span>
* ALPN: offers h2
* ALPN: offers http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: none
* <span class="o">[</span>CONN-0-0<span class="o">][</span>CF-SSL<span class="o">]</span> TLSv1.0 <span class="o">(</span>OUT<span class="o">)</span>, TLS header, Certificate Status <span class="o">(</span><span class="m">22</span><span class="o">)</span>:
* <span class="o">[</span>CONN-0-0<span class="o">][</span>CF-SSL<span class="o">]</span> TLSv1.3 <span class="o">(</span>OUT<span class="o">)</span>, TLS handshake, Client hello <span class="o">(</span><span class="m">1</span><span class="o">)</span>:
* SSL connection timeout
* Closing connection <span class="m">0</span>
curl: <span class="o">(</span><span class="m">28</span><span class="o">)</span> SSL connection timeout
</code></pre></div>
<h1>Is the source network the cause?</h1>
<p>Since it just works from some networks and not from others.
The interesting question is:</p>
<ul>
<li>Is the telegram network to blame? Is there a bug/configuration depending on some client networks?</li>
<li>Or is there a middle box droppping traffic?</li>
</ul>
<p>The routing works and since HTTP without encryption works I suspect at least one middle box doing something weird with TLS 1.3 instead of just forwarding the data.</p>Optimise sqlite3 1.4 million rows access2023-01-12T00:00:00+01:002023-01-12T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2023-01-12:/2023/1/12/optimise-sqlite3-14-million-rows-access/<p>I am running a small <a href="https://www.python.org/">python3</a> service that checks some TLS certificates with a <a href="https://wiki.archlinux.org/title/Systemd/Timers">systemd timer …</a></p><p>I am running a small <a href="https://www.python.org/">python3</a> service that checks some TLS certificates with a <a href="https://wiki.archlinux.org/title/Systemd/Timers">systemd timer</a>.
Over the months it accumulated over 1.4 million rows and the memory consumption rose to over 750MB per check run.</p>
<p>The initial code was like this:</p>
<div class="highlight"><pre><span></span><code><span class="n">db</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">'database.sqlite3'</span><span class="p">)</span>
<span class="n">cur</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">cur</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">'SELECT timestamp, hostname, common_name, alt_names FROM samples ORDER BY id DESC'</span><span class="p">)</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">cur</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">rows</span><span class="p">:</span>
<span class="c1"># do the things</span>
<span class="n">db</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div>
<p>The biggest problem was this line: <code>rows = cur.fetchall()</code>
It did copy the entire database into memory.
Somehow a 253MB sqlite3 file occupies over 750MB in memory.</p>
<p>A small speedup was changing the <code>isolation_level</code>, since I know that the database will not get accessed by any other program.</p>
<p>Using the <code>with closable as db:</code> automatically flushes and closes the database connection.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># EXCLUSIVE prevents other database connections from reading the database while the transaction is underway. </span>
<span class="k">with</span> <span class="n">sqlite3</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">'database.sqlite3'</span><span class="p">,</span> <span class="n">isolation_level</span><span class="o">=</span><span class="s1">'Exclusive'</span><span class="p">)</span> <span class="k">as</span> <span class="n">db</span><span class="p">:</span>
<span class="n">cur</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="n">rows</span> <span class="o">=</span> <span class="n">cur</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s1">'SELECT timestamp, hostname, common_name, alt_names FROM samples ORDER BY id DESC'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">rows</span><span class="p">:</span>
<span class="c1"># do the things</span>
</code></pre></div>
<p>With these tweaks I was able to reduce the memory footprint from 750MB to 37MB and the runtime from 3.7s to 2.9s 🎉</p>How to secure a hardened, minimal QNAP installation with letsencrypt2022-12-07T00:00:00+01:002022-12-07T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-12-07:/2022/12/7/how-to-secure-a-hardened-minimal-qnap-installation-with-letsencrypt/<p>There is a simple rule for hardening a system: You can not hack what is not …</p><p>There is a simple rule for hardening a system: You can not hack what is not there.</p>
<p>With this in mind, removing as much software from a system as possible makes it simpler to understand and then control.</p>
<p>For this guide you will need a <a href="https://en.wikipedia.org/wiki/Domain_Name_System">DNS</a> name.
You can get one for <a href="https://desec.io/">free from deSEC</a> or register your own.</p>
<h1>Why do this extra work?</h1>
<p>Because QNAP has a terrible track record on security.
They trade more features for understandability.
Violating the <a href="https://en.wikipedia.org/wiki/KISS_principle">KISS principle</a> or <a href="https://www.cisa.gov/uscert/bsi/articles/knowledge/principles/economy-of-mechanism">Economy of mechanism</a> a key part of system security.</p>
<p>Some of the last attacks were using the default webserver to access privileged services.
Disabling it closes that attack vector.</p>
<h1>Setup NAS</h1>
<h2>Assign a static IP</h2>
<p>Assign a static IPv4 and a IPv6 address to all the adapters or bridges you use.
Even if you use private IPs only, the DHCP server can forget about your current lease and assign the NAS a new IP and then a lot of stuff breaks.</p>
<p><em>Side note:</em> It is good practice to have your static (server) assignments outside of your dynamic range.</p>
<p>Example:</p>
<ul>
<li>Router: 10.0.0.1/24 ; fd02:cafe::1/64</li>
<li>Dynamic Range: 10.0.0.100 - 10.0.0.200 ; fd02:cafe::d:0 - fd02:cafe::d:ff</li>
<li>Static Range: 10.0.0.10 - 10.0.0.99 ; fd02:cafe::10 - fd02:cafe::ff</li>
</ul>
<p>Or use a globally unique IPv6 range from ISP or your datacenter (<a href="https://ipv6onlyhosting.com/">eg. ipv6onlyhosting</a>) and never worry about overlapping ip ranges again when connecting networks (with VPN).</p>
<h2>Remove or Stop already installed apps</h2>
<p>Go to <code>App Center</code> then remove everything you don't need except:</p>
<ul>
<li>QuFirewall (install it if you don't have it)</li>
<li>License Center (sadly)</li>
<li>Malware Remover<ul>
<li>Disable periodic scans</li>
<li>Set to auto update</li>
<li>Enable scan on update</li>
</ul>
</li>
<li>Network & Virtual Switch</li>
<li>Notification Center</li>
<li>QTS SSL Certifiacte</li>
<li>QuLog Center</li>
<li>Resource Monitor</li>
</ul>
<p>Most people will also keep
* Container Station (docker with docker-compose)
* Virtualization Station (kvm)</p>
<p>Everything else can go or be replaced by containers like replace Video Station with <a href="https://hub.docker.com/r/jellyfin/jellyfin">jellyfin in a container</a></p>
<h3>Minimal Configuration</h3>
<p>Go to <code>ControlPanel</code> -> <code>System</code> and set the following settings:</p>
<ul>
<li>General Settings<ul>
<li>Enable secure connection (HTTPS)</li>
<li>TLS version: <code>1.2 and later</code></li>
<li>Enable strong cipher suites</li>
<li>Port number: <code>443</code></li>
</ul>
</li>
</ul>
<h2>Enable SSH Access on QNAP</h2>
<p>Login with an admin account to your NAS</p>
<p>Then open <code>ControlPanel</code> -> <code>Network & File Services</code> -> <code>Telnet / SSH</code></p>
<p>Now enable:</p>
<ul>
<li>Allow SSH connection (Only administrators can login remotely.)</li>
<li>Port number: 22</li>
<li>Enable SFTP</li>
</ul>
<h1>Open Firewall (a bit)</h1>
<p>Here we need two firewalls to work in agreement.</p>
<h3>Router or Edge Firewall</h3>
<p>This is the firewall or router between your NAS and the internet.
If you don't have one already, you should get one now!</p>
<h4>Hardware</h4>
<p>Using an old PC with a PCIe Ethernet card is an okay option.</p>
<p>If you can get an <a href="https://pcengines.ch/apu2.htm">APU2</a> or a <a href="https://www.turris.com/en/">Turris</a> or a <a href="https://mikrotik.com">mikrotik</a>, even better.</p>
<h4>Software</h4>
<p><a href="https://opnsense.org/">opnsense (OSS)</a> or <a href="https://www.pfsense.org/">pfsense (mostly OSS)</a> or <a href="https://mikrotik.com/">RouterOS from MicroTik (pay one time)</a> are popular choices these days. </p>
<h4>Rules</h4>
<ol>
<li>Forbid all incoming traffic as the last rule.</li>
<li>Then add an exception for HTTPS or port <code>tcp/443</code></li>
<li>For the duration of confirming the certificate add an exception for HTTP or port <code>tcp/80</code></li>
</ol>
<p>Both rules are </p>
<ul>
<li><code>IN</code> rules that allow from</li>
<li>any source from IPv4 or IPv6 with</li>
<li>source port range 1025..65535</li>
<li>optional: rate limit new connections to 20 per 90 seconds</li>
</ul>
<h3>On Device Firewall QuFirewall</h3>
<p>You can get the QuFirewall in the <code>App Center</code> for free.</p>
<p>Open the QuFirewall application and Add a new profile.</p>
<p>Then add all the IPv4 and IPv6 ranges that are allowed to use the NAS es services.</p>
<ul>
<li>Include Samba (the windows shares) on port 445 (probably only to LAN IPs)</li>
<li>HTTP on port 80 (from the everywhere <code>0.0.0.0/0</code> and <code>::/0</code>)</li>
<li>HTTPS on port 443 (also from everywhere</li>
<li>And all the ports you expose from containers</li>
</ul>
<p><strong>Important:</strong> KVM virtual machines with bridged interfaces need their own firewalls</p>
<hr>
<h2>Compile the binary</h2>
<div class="highlight"><pre><span></span><code><span class="c1">/// LICENSE: AGPLv3</span><span class="w"></span>
<span class="c1">/// Copyright (C) 2022 Stefan Schindler</span><span class="w"></span>
<span class="c1">/// https://estada.ch/2022/2/7/how-to-secure-a-minimal-qnap-installation-with-letsencrypt/</span><span class="w"></span>
<span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
<span class="w"> </span><span class="s">"net/http"</span><span class="w"></span>
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>
<span class="c1">/// get: `go tool dist list`</span><span class="w"></span>
<span class="c1">/// current: `go env GOOS GOARCH`</span><span class="w"></span>
<span class="c1">/// </span><span class="w"></span>
<span class="c1">/// build: `go build -tags netgo ./http_well_known.go`</span><span class="w"></span>
<span class="c1">/// </span><span class="w"></span>
<span class="c1">/// serve /mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known for letsencrypt</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">argsWithoutProg</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span><span class="p">[</span><span class="mi">1</span><span class="p">:]</span><span class="w"></span>
<span class="w"> </span><span class="nx">bind_addr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">":80"</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nb">len</span><span class="p">(</span><span class="nx">argsWithoutProg</span><span class="p">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">bind_addr</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">argsWithoutProg</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"binding to \"%s\"\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">bind_addr</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Chdir</span><span class="p">(</span><span class="s">"/mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">newDir</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Getwd</span><span class="p">()</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"Current Working Direcotry: %s\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">newDir</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">HandleFunc</span><span class="p">(</span><span class="s">"/"</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"%s: %s\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">"Welcome to my website!\n"</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">})</span><span class="w"></span>
<span class="w"> </span><span class="nx">fs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">FileServer</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nx">Dir</span><span class="p">(</span><span class="s">"./"</span><span class="p">))</span><span class="w"></span>
<span class="w"> </span><span class="nx">shifted_fs</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">StripPrefix</span><span class="p">(</span><span class="s">"/.well-known/"</span><span class="p">,</span><span class="w"> </span><span class="nx">fs</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="c1">// http.Handle("/.well-known/", shifted_fs)</span><span class="w"></span>
<span class="w"> </span><span class="c1">// Docs: https://pkg.go.dev/net/http</span><span class="w"></span>
<span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">HandleFunc</span><span class="p">(</span><span class="s">"/.well-known/"</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"%s: %s\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">Method</span><span class="p">,</span><span class="w"> </span><span class="cm">/*r.RequestURI*/</span><span class="w"> </span><span class="nx">r</span><span class="p">.</span><span class="nx">URL</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">shifted_fs</span><span class="p">.</span><span class="nx">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">})</span><span class="w"></span>
<span class="w"> </span><span class="nx">res</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ListenAndServe</span><span class="p">(</span><span class="nx">bind_addr</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">"%s\n"</span><span class="p">,</span><span class="w"> </span><span class="nx">res</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>Save this source code in your local user home under <code>~/go/bin</code> in <code>http_well_known.go</code>, you may have to create the directories.</p>
<p>Then compile it with this command:</p>
<div class="highlight"><pre><span></span><code>go build -tags netgo ./http_well_known.go
</code></pre></div>
<h2>Transfer the binary</h2>
<p>Note the <code>:</code> at the end is mandatory.</p>
<div class="highlight"><pre><span></span><code>scp http_well_known admin@qnas.estada.ch:
</code></pre></div>
<h2>Get/Renew the certificate</h2>
<p>First login with ssh on your machine:</p>
<div class="highlight"><pre><span></span><code>ssh admin@qnas.estada.ch
</code></pre></div>
<p>Qnap hides the fact that you can have an interactive shell in the options.
The <strong>8th</strong> invisible option is the <code>sh</code> shell.</p>
<p>Press <code>8</code> and then confirm with <code>y + Enter</code></p>
<p>Now run the server with <code>./http_well_known</code> it will output any access that takes place so you have a log if you are attacked meanwhile.</p>
<h3>Temporarily open all the firewalls</h3>
<p>Don't forget to open the all firewalls between the internet and the NAS to allow:</p>
<ul>
<li>IPv4 and IPv6</li>
<li>Source ports 1025..65535</li>
<li>Target ports 80, 443</li>
</ul>
<h3>Start the process in the WebUI</h3>
<p>Go to <code>ControlPanel</code> -> <code>System</code> -> <code>Security</code> -> <code>SSL Certifiacte & private Key</code>.
Then either:</p>
<ul>
<li>Press <code>Certifiacte Renewal</code> if the system is configured already</li>
<li>Press <code>Replace Certifiacte</code> if you want to add/change the TLS common/domain name<ul>
<li>Select <code>Get from letsencrypt</code> in the dropdown</li>
<li>Enter the domain name, eMail and all alternative names</li>
</ul>
</li>
</ul>
<p>Now you should see the network verify the challenge and verify the certificate:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># ./http_well_known </span>
binding to <span class="s2">":80"</span>
Current Working Direcotry: /mnt/HDA_ROOT/.config/QcloudSSLCertificate/cert/.well-known
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
GET: /.well-known/acme-challenge/q70a7XfBD6VVcno666666d2pD9abcdefICb3QH3Lfvw
</code></pre></div>
<p>After the new certificate is installed you can exit the <code>http_well_known</code> process by entering <code>Ctrl + C</code> and then <code>Ctrl + D</code>.</p>
<h2>Cleanup</h2>
<p>Under <code>ControlPanel</code> -> <code>System</code> -> <code>General Settings</code> -> <code>System Administration</code> enable:</p>
<ul>
<li>Force secure connection (HTTPS) only</li>
</ul>
<h3>Close all the firewalls</h3>
<ul>
<li>Close the QuFirewall</li>
<li>Close the firewall between the internet and the LAN</li>
</ul>Disable qnap App Center auto updates and do them yourself with a schedule2022-12-05T00:00:00+01:002022-12-05T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-12-05:/2022/12/5/disable-qnap-app-center-auto-updates-and-do-them-yourself-with-a-schedule/<p>Qnap has decided to enable automatic updates for apps as well.
This caused the admin interface …</p><p>Qnap has decided to enable automatic updates for apps as well.
This caused the admin interface to freeze up while I was away.</p>
<p>The only way to get it to work again was booting into the recovery mode over SSH, then updating the main firmware so it was compatible with the admin interface again.</p>
<h1>Setting up your schedule</h1>
<p>The NAS still needs to be maintained.
So every month allocate 2h for maintenance like this:</p>
<ol>
<li>Pick an evening or morning during the week that is free.</li>
<li>Setup a calendar entry like "every second Tuesday evening of the month at 19:30"</li>
<li>Add a reminder to it, like 30min and 6h before the event</li>
<li>Then login and do the work</li>
</ol>
<p>This means less down time for the NAS and no surprises.
Also, most of the time it requires less time and now you have some spare time 🎉</p>
<p>In addition, follow my <a href="https://estada.ch/2022/12/7/how-to-secure-a-hardened-minimal-qnap-installation-with-letsencrypt/">hardening guide</a> and reduce the attack surface of the NAS as much as possible.</p>
<h1>How to prevent future app update failures?</h1>
<p>First, disable the "App Center" auto updates.</p>
<ol>
<li>Login with an adminstrative user</li>
<li>Open "App Center"</li>
<li>Click "⚙️ Settings" then the tab "Updates"</li>
<li>Change the dropdown to "Send notification"</li>
</ol>
<h2>Stop automatic system updates</h2>
<ol>
<li>Use the global search for: "Firmware Update"</li>
<li>After the "Control Panel" has opened under "Firmware Update Settings"</li>
<li>Select "Notify me, but do not automatically update firmware"</li>
<li>Unter "Firmware update types" select all options</li>
</ol>Proposing a new key recovery algorithm for multiple decryption keys2022-11-17T00:00:00+01:002022-11-17T17:45:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-11-17:/2022/11/17/proposing-a-new-key-recovery-algorithm-for-multiple-decryption-keys/<p>This is part of my master thesis that I'm about to finish currently.</p>
<h1>The algorithm</h1>
<p><img alt="Master Key Decryption per Slot" src="https://estada.ch/2022/11/17/proposing-a-new-key-recovery-algorithm-for-multiple-decryption-keys/2022-11-17_proposing_a_new_keyrecovery_algorithm/MasterKeyDecryptionPerSlot.png"></p>
<p>I …</p><p>This is part of my master thesis that I'm about to finish currently.</p>
<h1>The algorithm</h1>
<p><img alt="Master Key Decryption per Slot" src="https://estada.ch/2022/11/17/proposing-a-new-key-recovery-algorithm-for-multiple-decryption-keys/2022-11-17_proposing_a_new_keyrecovery_algorithm/MasterKeyDecryptionPerSlot.png"></p>
<p>I am accepting key material from two sources:</p>
<ol>
<li>From a TPM, or Hardware-backed Keystore. There we need the whole 32 Bytes (256 bits) for maximum security.</li>
<li>From a Paper Key, or user input. Assuming we can get ~6 bits of randomness we need at least 42 characters. Going with the more pessimistic ~4.5 bits per character we need at least 56 characters to reach 256 bits strength.</li>
</ol>
<p>The next step is to calculate candidate values per <code>Slot</code>. </p>
<p>The <code>Slot</code> information is stored on disk and readable for an attacker.</p>
<p>Using the <code>Potential Slot Key</code> and the slot specific <code>Salt</code> I generate a <code>512 bits</code> key stream for the following components:</p>
<ol>
<li>The <code>Encrypted Master Key</code> from the first 256 bits.</li>
<li>The <code>Encrypted Nonce</code> from the next 128 bits, where I discard the first 32 bits because I need the lower 96 bits only.</li>
<li>The <code>Check Value</code> from the last 128 bits.</li>
</ol>
<p>If the <code>Check Value</code> matches the one stored on disk I know that the user input is valid and I can decrypt the super block with chacha20-poly1305.</p>
<h1>Why so complicated?</h1>
<p>The idea is that a user can synchronise the encrypted data over multiple devices and access it with convenience of the platforms hardware security.</p>
<p>Even without synchronisation firmware upgrade can erase the contents of a TPM and we need the <code>Paper Key</code> as a means to open the vault and include a new key with the current TPM.</p>
<h1>Open Question</h1>
<p>One risk is that an attacker is able to use the <code>Check Value</code> generated by <a href="https://docs.rs/argon2/">Argon2</a> to reverse back to the <code>Encrypted Nonce</code> or even <code>Encrypted Master Key</code>.</p>
<h1>Related Work</h1>
<p>I am inspired by the design of LUKS1 and LUKS2.</p>
<h1>Future Work</h1>
<p>The 32 bits that are not used in the <code>Slot.Decryption Nonce XOR</code> could be used for error detection.
This would not stop an attacker but allow the user to detect bit rot.</p>i18n with rust at Rust Zürisee 🦀 announcement 2022-12-072022-11-09T00:00:00+01:002022-11-11T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-11-09:/2022/11/9/i18n-with-rust-at-rust-zurisee-announcement-2022-12-07/<!--
Modified: 2022-12-07
-->
<p>We are back! <a href="https://www.meetup.com/de-DE/rust-zurich/events/289518586/">Register on meetup now!</a></p>
<p>The topic is internationalization i18n with rust, the translation …</p><!--
Modified: 2022-12-07
-->
<p>We are back! <a href="https://www.meetup.com/de-DE/rust-zurich/events/289518586/">Register on meetup now!</a></p>
<p>The topic is internationalization i18n with rust, the translation of ones application.
This time we are hosted by Google Zürich in the EURB room near the main station.</p>
<p>Because of the nature of the building we have to enter together as one group.
Late entry will not be possible.</p>
<p>Our meeting point is in front of</p>
<blockquote>
<p>Orell Füssli <br>
Europaallee 8<br>
8004 Zürich</p>
</blockquote>
<div class="embed ratio-16-9"><iframe frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://www.openstreetmap.org/export/embed.html?bbox=8.534590601921083%2C47.37753128341812%2C8.535550832748415%2C47.37906421575338&layer=mapnik&marker=47.378297755156694%2C8.535070717334747" style="border: 1px solid black"></iframe></div>
<p><small><a href="https://www.openstreetmap.org/?mlat=47.37830&mlon=8.53507#map=19/47.37830/8.53507">View Larger Map</a></small></p>
<h1>Agenda</h1>
<ul>
<li>18:45 → Gathering and welcoming attendees to Google by <a href="https://github.com/younies">Younies Mahmoud</a></li>
<li>19:00 → Doors open</li>
<li>19:15 → Rust Meetup introduction by <a href="https://github.com/dns2utf8">Stefan Schindler</a></li>
<li>
<p>19:20 → Rust in Google introduction and agenda introduction by <a href="https://github.com/mgeisler/">Martin Geisler</a></p>
</li>
<li>
<p>19:30 → <strong>Talk 1: <a href="https://github.com/sffc">Shane F. Carr @sffc</a></strong> <br>
Shane Carr, chair of the ICU4X subcommittee at the Unicode Consortium, will introduce ICU4X and discuss a range of topics on how to write a fast, modular Rust library.
Across the world, people are coming online with smartphones, smart watches, and other small, low-resource devices. The technology industry needs an internationalization solution for these environments that scales to dozens of programming languages and thousands of human languages.
Enter ICU4X. Built from the ground up to be lightweight, portable, and secure, ICU4X learns from decades of experience to bring localized date formatting, number formatting, collation, text segmentation, and more to devices that, until now, did not have a suitable solution.</p>
</li>
<li>
<p>20:15 → <strong>Break</strong></p>
</li>
<li>
<p>20:30 → <strong>Talk 2: <a href="https://github.com/Manishearth">Manish Goregaokar @manishearth</a></strong> <br>
Internationalization involves a lot of data. A Lot. And loading data can be slow.
ICU4X has dealt with this by going all-in on zero-copy deserialization, a technique that ensures that data loading involves fast validation as opposed to slow allocation. To help with this, we have built a suite of crates (yoke, zerovec, zerofrom, and databake) that make zero-copy deserialization easier to work with and more broadly applicable. This talk will tour through these crates, talking about what they do, how they work, and how they can be useful to you.</p>
</li>
<li>21:05 → Socialising</li>
<li>22:30 → Starting to close and goodbye 🙂</li>
</ul>
<h1>Future</h1>
<p>I am planing another meetup in February.
Would you like to present something small in 5 minutes? or big in up to 45 minutes?</p>
<p>Please contact me on our matrix channel: <a href="https://matrix.to/#/#rust-zuerisee:matrix.coredump.ch">#rust-zuerisee:matrix.coredump.ch</a></p>
<p><br/></p>XFS fsfreeze bug appears to be fixed in kernel 5.10.x2022-09-12T00:00:00+02:002022-09-12T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-09-12:/2022/9/12/xfs-fsfreeze-bug-appears-to-be-fixed-in-kernel-510x/<p>Over two years ago I discovered a <a href="https://bugzilla.kernel.org/show_bug.cgi?id=205833">kernel bug #205833</a> where a <code>close</code> operation on a …</p><p>Over two years ago I discovered a <a href="https://bugzilla.kernel.org/show_bug.cgi?id=205833">kernel bug #205833</a> where a <code>close</code> operation on a frozen <code>xfs</code> mount would trigger the trim operation.
This means that the filesystem can truncate away blocks beyond EOF of the file that it has reserved previously.
This is a write operation and the operation correctly blocks your process.</p>
<p>However, the truncate operation should not have been started in the first place on the mount the has the <code>fsfreeze</code> active.</p>
<p>I stumble over this bug while I was working for a client in 2019/2020 on their kubernetes backup solution that I built for them with <a href="https://velero.io/">Velero</a> and <a href="https://restic.net/">Restic</a>.
The goal was: Backing up the data volumes of applications that manage a folder <strong>atomically</strong> and do not have an export command.
With <code>xfs</code> however, this lead to the bug and hanging backup jobs.</p>
<h1>Two years later: I verify</h1>
<p>Today, I switched away from Ubuntu completely.
Arch for development and Debian with systemd on the servers has been a great pattern for me.
On the latest Arch Linux I am no longer able to reproduce the error where the second process hangs.</p>
<p>While in the last two years the default appears to have shifted from <code>xfs</code> to <code>ext4</code> most tutorials seem to still favour <code>xfs</code></p>
<h1>Testing</h1>
<p>To verify the problems I have created two projects that can run on any Linux.
As of my own testing <strong>Debian 11</strong> and <strong>Arch Linux</strong> since August 2022 no longer suffer from this bug.</p>
<p>If you are running a kubernetes cluster and your backup freezes for no apparent reason, this might be it.</p>
<p>Feel free to use my two tools and consider supporting my work. Thank you!</p>
<ul>
<li>Quick test: <a href="https://gitlab.com/dns2utf8/xfs_fsfreeze_test/">https://gitlab.com/dns2utf8/xfs_fsfreeze_test/</a></li>
<li>Heavy load: <a href="https://gitlab.com/dns2utf8/multi_file_writer/">https://gitlab.com/dns2utf8/multi_file_writer/</a></li>
</ul>Rust programming language in the high-performance computing environment 🦀 2022-05-062022-04-25T00:00:00+02:002022-04-25T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-04-25:/2022/4/25/rust-programming-language-in-the-high-performance-computing-environment-2022-05-06/<p><a href="https://www.linkedin.com/in/michalsudwoj/">Michal</a> will present his Bachelor Thesis at D-MATH, ETH Zürich.
He compares multiple implementations of a …</p><p><a href="https://www.linkedin.com/in/michalsudwoj/">Michal</a> will present his Bachelor Thesis at D-MATH, ETH Zürich.
He compares multiple implementations of a finite difference stencil code, and show that idiomatically written Rust programs can be just as performant as their Fortran or C++ counterparts.</p>
<p>Thanks to Gery this talk will be live in Zürich. 🥳
Register to the <a href="https://www.meetup.com/Rust-Zurich/events/285457518/">meetup event</a> to get the exact location.</p>
<p>We are looking for more speakers. We would love to host you in person or over the internet :)
Please contact us if you have a proposal or would like to propose someone else that might be interested.
If you have a topic that interests you, feel free to message us in our matrix channel: #rust-zuerisee:matrix.coredump.ch</p>UHK keyboard maintenance2022-04-02T00:00:00+02:002022-04-02T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-04-02:/2022/4/2/uhk-keyboard-maintenance/<p>I am using the first version of <a href="https://ultimatehackingkeyboard.com/">the split keyboard from UHK</a> with the wooden palm …</p><p>I am using the first version of <a href="https://ultimatehackingkeyboard.com/">the split keyboard from UHK</a> with the wooden palm rests since 2018. </p>
<p>The UHK have a staggered layout like the most common keyboards.
That makes the switch to the split very easy as all the muscle memory still works the same.
Well, except for the <code>B</code> key, because I learned to use that key with the right hand and it is on the left keyboard half.</p>
<h1>Maintenance on the trackball</h1>
<p>Last year the manufacturer delivered the extension modules key-cluster and trackball.
At first it was great, not having to use the mouse layer for steering the mouse is a huge ergonomic improvement.</p>
<p>Then the trackball started bugging out.
The faults were pretty random but became more and more common over time.</p>
<p>Finally, I opened up the module and found lumped flakes of dust as you can see in the picture.
I assume that these flakes were stuck for most of the time but sometimes got loose and then covered the sensor (in the middle of the module).</p>
<p>Since I cleaned the module, the ball and the ring with a dry kitchen paper the module works again perfectly 🥳</p>
<p><img alt="Dirty Trackball Module" src="https://estada.ch/2022/4/2/uhk-keyboard-maintenance/2022-04-02_uhk-cleaning/trackball.webp"></p>
<h2>Setting a reminder</h2>
<p>To prevent the module from failing in the future I added a yearly reminder in my calendar to clean it.
So I can forget it while making sure the keyboard keeps on working.</p>
<h1>My next keyboard</h1>
<p>Honestly, I don't plan to buy a new keyboard for some time. The UHK is very sturdy.</p>
<p>However, the next one will probably be an Ortholinear one like ergodox <a href="https://www.ergodox.io/">DIY</a> or <a href="https://ergodox-ez.com/">EZ</a> or the <a href="https://www.zsa.io/moonlander/">moonlander</a>.</p>How to unstuck your Ubuntu Linux kernel updates2022-02-06T00:00:00+01:002022-02-06T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-02-06:/2022/2/6/how-to-unstuck-your-ubuntu-linux-kernel-updates/<h2>Identify that you have this problem checklist</h2>
<p><strong>This is very important:</strong> Applying this fix is a …</p><h2>Identify that you have this problem checklist</h2>
<p><strong>This is very important:</strong> Applying this fix is a wast of your time if that is not your problem.</p>
<ul>
<li><label><input type="checkbox"/><span> <code>df -h /boot</code> shows 100% usage</span></label></li>
<li><label><input type="checkbox"/><span> <code>apt upgrade</code> or <code>apt dist-upgrade</code> or <code>apt-get upgrade</code> or <code>dpkg --configure -a</code> are complaining about the package <code>linux</code></span></label></li>
<li><label><input type="checkbox"/><span> You have a terminal to the system open (called <em>Terminal A</em>)</span></label></li>
<li><label><input type="checkbox"/><span> You have a <strong>second</strong> terminal to the system open (called <em>Terminal B</em>)</span></label></li>
</ul>
<h2>Become root with sudo (Terminal A & B)</h2>
<p>If you are not logged in as <code>root</code> via <a href="https://wiki.archlinux.org/title/Secure_Shell">ssh</a> already:</p>
<div class="highlight"><pre><span></span><code>sudo -i
</code></pre></div>
<p>Do this in both terminals.</p>
<h2>Empty the existing images (Terminal A)</h2>
<p>First, decide which kernels are no longer needed:</p>
<div class="highlight"><pre><span></span><code><span class="nb">cd</span> /boot
ls -hasl
</code></pre></div>
<p>Second, assuming we have multiple versions of the <code>4.15.0</code> kernel, for example (160, 163, 166, 167, 169)</p>
<ul>
<li><label><input type="checkbox"/><span> Get the current kernel <strong><em>to keep</em></strong>: <code>uname -r</code></span></label> <input type="text" placeholder="write version down here"/><ul>
<li><label><input type="checkbox"/><span> the current version is something like <code>5.4.0</code></span></label></li>
</ul>
</li>
<li><label><input type="checkbox"/><span> Empty the old <a href="https://wiki.ubuntu.com/Initramfs">initramfs</a> in a loop <code>watch "df -h . ; echo | tee initrd.img-4.15.0-16*"</code> because apt on Ubuntu recreates these files during the update process all the time (yes even when you remove a kernel)</span></label></li>
</ul>
<h2>Remove old kernels (Terminal B)</h2>
<p>The simplest way is to have apt remove old versions for us:</p>
<ul>
<li><label><input type="checkbox"/><span> Now there must be some space left again, check with <code>df -h /boot</code></span></label></li>
<li><label><input type="checkbox"/><span> Remove the old versions with <code>sudo apt autoremove</code></span></label></li>
</ul>
<p>Now <code>apt</code> can remove all unused kernels not just the <code>4.15.0-*</code> ones.</p>
<h2>Cleanup old empty files (Terminal A)</h2>
<ul>
<li><label><input type="checkbox"/><span> Stop the watch process with <code>Ctrl + C</code></span></label></li>
<li><label><input type="checkbox"/><span> Find the file with size <code>1</code> with <code>ls -hasl /boot</code> (should be just one, could be more)</span></label></li>
<li><label><input type="checkbox"/><span> Remove the old image file with a command like this <code>rm /boot/initrd-4.15.0-167-generic</code></span></label></li>
</ul>
<h2>Cleanup & Future</h2>
<p>Leave the root shells with <code>exit</code> or <code>Ctrl + D</code> and enjoy that you fixed a system.</p>
<p>Consider this command for updating your system in the future and avoid having this problem again:</p>
<div class="highlight"><pre><span></span><code>df -h <span class="o">&&</span> apt update <span class="o">&&</span> apt dist-upgrade <span class="o">&&</span> apt autoremove
</code></pre></div>
<p><link rel="stylesheet" type="text/css" href="https://estada.ch/2022/2/6/how-to-unstuck-your-ubuntu-linux-kernel-updates/2022-02-06_unstuck-linux-kernel-updates.css" /></p>How to install cargo-outdated in OSX without homebrew2022-01-07T00:00:00+01:002022-01-07T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2022-01-07:/2022/1/7/how-to-install-cargo-outdated-in-osx-without-homebrew/<p>TLDR:</p>
<div class="highlight"><pre><span></span><code>cargo install --force --features vendored-openssl cargo-outdated
</code></pre></div>
<h1>Developing with Rust on macOS</h1>
<p>Installing rust with <a href="https://rustup.rs/">rustup …</a></p><p>TLDR:</p>
<div class="highlight"><pre><span></span><code>cargo install --force --features vendored-openssl cargo-outdated
</code></pre></div>
<h1>Developing with Rust on macOS</h1>
<p>Installing rust with <a href="https://rustup.rs/">rustup</a> is very simple and development with
<a href="https://code.visualstudio.com/">VSCode</a> and git (type <code>git</code> and press <code>Enter</code> in a terminal and follow the instructions)
work great.</p>
<p>Until you hit an openssl dependency.</p>
<h2>Going the native way</h2>
<p><a href="https://crates.io/crates/native-tls">Using native-tls</a> abstracts the specific details away from the platforms and on desktop systems "just works" (usually)</p>
<p>For most applications this is a very good strategy to reduce the maintenance workload while remaining portable.</p>
<h2>Going the vendored way</h2>
<p>You can not rely on the platforms and distributions?
You can ship your own and vendor everything.</p>
<p><a href="https://crates.io/crates/cargo-vendor">With cargo-vendor</a> you can automate the process on your machine and store
all your dependencies in your git repository.
You want to keep an eye out for all the licenses used if you are a business.</p>
<h2>Going the homebrew way</h2>
<p><a href="https://brew.sh/">Homebrew</a> tries to turn your macOS into a more Unix/Linux environment.
You get a package manager where you install openssl or anything else you need <strong>but</strong> now you have to maintain two systems on the same machine.</p>
<p>Windows users know this state from <a href="https://docs.microsoft.com/en-us/windows/wsl/install">WSL and WSL2 (Windows Subsystem for Linux)</a>
and Linux users from using <a href="https://www.winehq.org/">wine</a></p>Updating minecraft to mitigate log4j - got a new log4j RCE - and how I would go forward2021-12-18T00:00:00+01:002021-12-18T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-12-18:/2021/12/18/updating-minecraft-to-mitigate-log4j-got-a-new-log4j-rce-and-how-i-would-go-forward/<p>Now, this is not meant to be funny.
I read about the log4j debacle on the …</p><p>Now, this is not meant to be funny.
I read about the log4j debacle on the news and was relieved that the only java based service I operate is one minecraft instance.</p>
<p><a href="https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition">Here is the official minecraft blog post</a> that basically tells you to update to <code>1.18.1</code> or apply a workaround.
There is <a href="https://help.minecraft.net/hc/en-us/articles/4416199399693-Security-Vulnerability-in-Minecraft-Java-Edition">a second version</a> of the same article as well.</p>
<h1>Updating to the latest release</h1>
<p>Sadly, finding the binary on the official minecraft website without a search engine is next to impossible.
Here is the direct link for you: <a href="https://www.minecraft.net/download/server">https://www.minecraft.net/download/server</a></p>
<p>And after the update I see that minecraft <code>1.18.1</code> uses log4j <code>2.14.1</code>.
Now, the precise readers note that this is still one of the affected releases.</p>
<h2>Clients</h2>
<p>The clients should update themselves after a complete restart of the application and launcher.
So that is good.</p>
<h1>log4j 2.16.0</h1>
<p>On the <a href="https://msrc-blog.microsoft.com/2021/12/11/microsofts-response-to-cve-2021-44228-apache-log4j2/">Microsoft security response center they detail</a> how this <code>JndiLookup</code> can be exploited and how to delete the class from the jar file in case there is no legitimate use for it in the application.</p>
<h1>log4j 2.17.0 and DoS</h1>
<p>This release <a href="https://issues.apache.org/jira/browse/LOG4J2-3230"><strong>should</strong> fix infinite recursion</a> but as some pointed out: <a href="https://www.bleepingcomputer.com/news/security/upgraded-to-log4j-216-surprise-theres-a-217-fixing-dos/">maybe not</a></p>
<p>You can download the latest version here: <a href="https://logging.apache.org/log4j/2.x/download.html">https://logging.apache.org/log4j/2.x/download.html</a></p>
<h1>Minecraft Server in the future</h1>
<p>For the minecraft operators, wait for <code>1.18.2</code> and hope that it contains a more recent version that contains all the fixes.</p>
<p>Running services on the internet is always risky and very useful.
So one has to make the best efforts to keep them secure and operational.</p>
<h1>What now?</h1>
<p>Well as <a href="https://heise.de/-6294476">heise put it: works as specified</a></p>
<p>The function with the lookup in the string that is to be logged falls in the same category as SQL injections.
Both build on the same error:</p>
<ul>
<li>Have multiple contexts (control and data) in one and</li>
<li>Let remote attackers control the one of them (data) that ends up in the parsed string</li>
</ul>
<p>With SQL the solution was a change in protocol where we have placeholders now and the untrusted data is submitted separately</p>
<h2>How did SQL fix it?</h2>
<p>The answer is <a href="https://www.postgresql.org/docs/14/ecpg-dynamic.html#ECPG-DYNAMIC-INPUT">Input Parameters</a>.
Basically, instead of having control and data in the same string we have the <code>?</code> to indicate where in the parsed AST we want to insert dynamic data and in a second step we actually send the data so we do not have any confusion on what is what.</p>
<p>To quote from the PostgreSQL manual, here is how to use a named stored procedure:</p>
<div class="highlight"><pre><span></span><code><span class="k">EXEC</span><span class="w"> </span><span class="k">SQL</span><span class="w"> </span><span class="k">BEGIN</span><span class="w"> </span><span class="k">DECLARE</span><span class="w"> </span><span class="n">SECTION</span><span class="p">;</span><span class="w"></span>
<span class="n">const</span><span class="w"> </span><span class="nb">char</span><span class="w"> </span><span class="o">*</span><span class="n">stmt</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ss">"INSERT INTO test1 VALUES(?, ?);"</span><span class="p">;</span><span class="w"></span>
<span class="k">EXEC</span><span class="w"> </span><span class="k">SQL</span><span class="w"> </span><span class="k">END</span><span class="w"> </span><span class="k">DECLARE</span><span class="w"> </span><span class="n">SECTION</span><span class="p">;</span><span class="w"></span>
<span class="k">EXEC</span><span class="w"> </span><span class="k">SQL</span><span class="w"> </span><span class="k">PREPARE</span><span class="w"> </span><span class="n">mystmt</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="p">:</span><span class="n">stmt</span><span class="p">;</span><span class="w"></span>
<span class="k">EXEC</span><span class="w"> </span><span class="k">SQL</span><span class="w"> </span><span class="k">EXECUTE</span><span class="w"> </span><span class="n">mystmt</span><span class="w"> </span><span class="k">USING</span><span class="w"> </span><span class="mi">42</span><span class="p">,</span><span class="w"> </span><span class="s1">'foobar'</span><span class="p">;</span><span class="w"></span>
</code></pre></div>
<p>And here is an example from <a href="https://docs.microsoft.com/en-us/sql/relational-databases/query-processing-architecture-guide?view=sql-server-ver15">MS SQL Server</a> with Parameter Binding:</p>
<div class="highlight"><pre><span></span><code><span class="n">SQLExecDirect</span><span class="p">(</span><span class="n">hstmt</span><span class="p">,</span><span class="w"> </span>
<span class="w"> </span><span class="s">"SELECT * FROM AdventureWorks2014.Production.Product </span>
<span class="w"> </span><span class="n">WHERE</span><span class="w"> </span><span class="n">ProductSubcategoryID</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="p">?</span><span class="s">",</span>
<span class="w"> </span><span class="n">SQL_NTS</span><span class="p">);</span><span class="w"></span>
</code></pre></div>
<h2>Less is more</h2>
<p>Another solution is to simply remove the functionality all together.
You can not hack what is not there.</p>How to upgrade MacOSX Big Sur to Monterey with an encrypted drive2021-12-08T00:00:00+01:002021-12-08T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-12-08:/2021/12/8/how-to-upgrade-macosx-big-sur-to-monterey-with-an-encrypted-drive/<p><img alt="Updater Interface" src="https://estada.ch/2021/12/8/how-to-upgrade-macosx-big-sur-to-monterey-with-an-encrypted-drive/2021-12-08_macosx-12-monterey-upgrade/Updater-stuck.png"></p>
<p>I am trying to upgrade an old laptop with the latest OS from apple.
Sadly, the …</p><p><img alt="Updater Interface" src="https://estada.ch/2021/12/8/how-to-upgrade-macosx-big-sur-to-monterey-with-an-encrypted-drive/2021-12-08_macosx-12-monterey-upgrade/Updater-stuck.png"></p>
<p>I am trying to upgrade an old laptop with the latest OS from apple.
Sadly, the current upgrade program is unable to do so if the main partition/volume/container is encrypted.</p>
<h1>Add a new Volume with Disk Utility</h1>
<p>In disk utility click on you current container then click the little plus to create a new volume:</p>
<p><img alt="Add new Volume" src="https://estada.ch/2021/12/8/how-to-upgrade-macosx-big-sur-to-monterey-with-an-encrypted-drive/2021-12-08_macosx-12-monterey-upgrade/disk-utility-new-container.png"></p>
<p>Now type a new name, eg. "monterey", select <code>APFS (Upper-/Lowercase)</code> and confirm with <code>Add</code></p>
<h1>Continue the installation</h1>
<p>Back in the Upgradeer the new volume should have been recognise already.
Select it to continue the process while keeping your data secure.</p>
<h1>Future questions</h1>
<p>The biggest question is, can the new volume be encrypted in the future to secure the OS as well as the data?</p>
<p>At the time of writing, I can not answer this.
I will look into it in the coming days.
Consider subscribing to my <a href="/feeds/all.atom.xml">RSS/Atom Feed</a> so you don't miss it.</p>Zoom schaltet Werbung -> Wechseln wir zu Jitsi2021-11-02T00:00:00+01:002021-11-02T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-11-02:/2021/11/2/zoom-schaltet-werbung-wechseln-wir-zu-jitsi/de/<p>Zoom will Werbung in der "kostenlosen" Variante schalten.
Dass der Service kosten verursacht war klar, bisher …</p><p>Zoom will Werbung in der "kostenlosen" Variante schalten.
Dass der Service kosten verursacht war klar, bisher hat Zoom diese einfach mit Kapital aus anderen Quellen (evt. VC? Ihr <a href="https://investors.zoom.us/news-releases/news-release-details/zoom-reports-financial-results-second-quarter-fiscal-year-2022">Finanzbericht vom August ist da nicht transparent</a>) bezahlt.</p>
<p>Die <a href="https://explore.zoom.us/en/privacy/">Zoom AGB</a> machen keinen Unterschied zwischen der Bezahlten- und der Gratis-Version und sie räumen sich unter anderem diese Rechte ein:</p>
<ul>
<li>Datensammeln für:</li>
<li>Marketing, Promotions, and Third-Party Advertising</li>
</ul>
<p>Und das machen sie auch mit ihren Geschäftspartnern:</p>
<ul>
<li>Resellers</li>
<li>Vendors</li>
<li>Marketing, Advertising, and Analytics Partners</li>
<li>Corporate Affiliates</li>
</ul>
<p>Immerhin sollte man mindestens 16 Jahre alt sein:</p>
<div class="highlight"><pre><span></span><code><span class="nv">Zoom</span><span class="w"> </span><span class="nv">does</span><span class="w"> </span><span class="nv">not</span><span class="w"> </span><span class="nv">allow</span><span class="w"> </span><span class="nv">children</span><span class="w"> </span><span class="nv">under</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">age</span><span class="w"> </span><span class="nv">of</span><span class="w"> </span><span class="mi">16</span><span class="w"> </span><span class="nv">to</span><span class="w"> </span><span class="nv">sign</span><span class="w"> </span><span class="nv">up</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">Zoom</span><span class="w"> </span><span class="nv">account</span>.<span class="w"></span>
</code></pre></div>
<h2>Was für Konsequenzen hat das?</h2>
<p>Wie (fast) jedes Mal wenn digitale Werbung geschaltet wird sie "personalisiert".
Das heisst, es fliessen Profil-Daten und Metadaten zu den Handelsplätzen ab, wo die verschiedenen Werbe-Netzwerke um den Werbeplatz bieten.</p>
<p>Damit wird die Privatsphäre massiv aufgeweicht.</p>
<h1>Alternative <a href="https://jitsi.org/">Jitsi</a> (Open Source Software)</h1>
<p><a href="https://coredump.ch">Der Coredump Hackerspace in Rapperswil/Jona</a> betreibt eine öffentliche Instanz, welche komplett in der Schweiz gehostet ist:</p>
<ul>
<li><a href="https://meet.coredump.ch/">https://meet.coredump.ch/</a></li>
</ul>
<p>Auch <a href="https://www.switch.ch/de/meet/">Switch</a>, welche die Schweizer Hochschulgemeinschaft mit IT versorgt und unterstützt, betreibt einen grösseren Jitsi Cluster:</p>
<ul>
<li><a href="https://open.meet.switch.ch/">https://open.meet.switch.ch/</a></li>
</ul>
<h1>Weitere Alternativen</h1>
<p>Die einfachste Alternative ist einen anderen kommerziellen Anbieter zu finden, dessen Geschäftsmodell nicht darauf aufbaut die Daten und Meta-Daten der Benutzenden Menschen zu verkaufen.</p>
<p>Personal anstellen ist für grössere Firmen mittelfristig vermutlich die beste Option.
Aktuell qualifiziertes Personal zu finden ist möglich, wenn die Firma bereit ist einen entsprechenden Lohn zu bezahlen.</p>Unpacking a QEMU qcow2 image on a physical disk2021-08-01T00:00:00+02:002021-08-01T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-08-01:/2021/8/1/unpacking-a-qemu-qcow2-image-on-a-physical-disk/<p><img alt="Two thinkpads connected with Ethernet" src="https://estada.ch/2021/8/1/unpacking-a-qemu-qcow2-image-on-a-physical-disk/2021-08-01/ethernet_laptops.webp"></p>
<p>I have an old laptop with a slowish HDD so I did the smart thing and …</p><p><img alt="Two thinkpads connected with Ethernet" src="https://estada.ch/2021/8/1/unpacking-a-qemu-qcow2-image-on-a-physical-disk/2021-08-01/ethernet_laptops.webp"></p>
<p>I have an old laptop with a slowish HDD so I did the smart thing and installed an operating system inside a QEMU+KVM on my modern machine with much more RAM and a SSD.</p>
<p>Also, the WiFi drivers for that particular model are not included in most operating systems, so until they are fetched via Ethernet the device is pretty helpless.</p>
<p>Lucky me: <a href="https://wiki.archlinux.org/title/NetworkManager">NetworkManager</a> has a connection sharing feature that takes care of all the annoying details like IPv4 NAT, DNS forwarding and DHCPv4.
I wish it would work that easy with IPv6 too.</p>
<p>Now that I am finished, how do I get the image over to the physical drive?</p>
<h1>Attempt one: Sending through Pipe (does not work)</h1>
<p>QEMU comes with this handy conversion tool that should work like the real <code>dd</code>:</p>
<div class="highlight"><pre><span></span><code>qemu-img dd -f qcow2 -O raw <span class="nv">bs</span><span class="o">=</span>4M <span class="k">if</span><span class="o">=</span>hda.qcow2 <span class="nv">of</span><span class="o">=</span>- <span class="p">|</span> ssh root@10.42.0.247 <span class="s1">'dd bs=4M status=progress of=/dev/sda'</span>
</code></pre></div>
<p>Sadly, no.
According to <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=513451">this bug report from 2009</a>:</p>
<div class="highlight"><pre><span></span><code>This is technically not possible. Given the formats involved, the file is not read/written linearly
</code></pre></div>
<p>Or as filesystem experts like to say:
Both input and output files need to be <strong>seekable</strong> and a pipe is not.</p>
<h1>Attempt two: sshfs (does not work)</h1>
<p>Instead of writing the file in a local buffer which would require a lot of free disk space, why not:</p>
<ol>
<li>boot the target machine with <a href="https://www.kali.org/">Kali Linux</a> or <a href="https://knopper.net/knoppix-mirrors/index-en.html">Knoppix</a></li>
<li>start sshd on the machine, give root a password and enable root login with password in <code>/etc/ssh/sshd_config</code></li>
<li>mount it like this: <code>sshfs root@10.42.0.247:/ /run/media/target_host/</code></li>
</ol>
<p>Now we have access to seekable files. </p>
<p>Sadly, sshfs does not support device files. So we can not write to <code>/dev/sda*</code></p>
<h1>Attempt three: sshfs back (works)</h1>
<p>So at this point it is just a matter of willpower.
A battle of wits if you will. 😉</p>
<p>First, install <a href="https://wiki.archlinux.org/title/Secure_Shell">sshd</a> on my main machine and make sure that it does <strong>not</strong> start on boot automatically.
Starting it on my machine with <code>sudo systemctl start sshd</code>.</p>
<p>Second, with Kali already running on the target machine:
1. <code>apt update</code> to get the latest packages
2. <code>apt install sshfs qemu-block-extra</code>
3. Mount backwards like this: <code>sshfs user@10.42.0.1:/path/to/my/img/ /mnt/</code>
4. Finally, apply the image to the disk with: <code>qemu-img dd -f qcow2 -O raw bs=4M if=/mnt/hda.qcow2 of=/dev/sda</code></p>
<p>After a quite substantial wait (roughly 45min, praise gigabit Ethernet) the complete image was transferred 🎉</p>
<p>What a time to be alive!</p>Experimenting with the new CO2 Laser Cutter at Coredump2021-07-26T00:00:00+02:002021-07-26T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-07-26:/2021/7/26/experimenting-with-the-new-co2-laser-cutter-at-coredump/<p>I did my first experiments with the new CNC CO₂ Laser.
<img alt="Wooden Rust Logo" src="https://estada.ch/2021/7/26/experimenting-with-the-new-co2-laser-cutter-at-coredump/2021-07-26_lasercnc/wooden_rust_logo.webp"></p>
<p>After finding the right settings …</p><p>I did my first experiments with the new CNC CO₂ Laser.
<img alt="Wooden Rust Logo" src="https://estada.ch/2021/7/26/experimenting-with-the-new-co2-laser-cutter-at-coredump/2021-07-26_lasercnc/wooden_rust_logo.webp"></p>
<p>After finding the right settings for engraving and cutting through plywood.</p>
<p>However, I had made a mistake with the order of the cuts:</p>
<ol>
<li>Light burn the lines</li>
<li>Cut out the whole piece</li>
<li>Lightly burning the filling colour</li>
</ol>
<p>So after the second series of cuts the piece fell down and was a bit loose.</p>
<p>Why is this a problem? The cutter has three cooling systems:</p>
<ol>
<li>Water Cooling the CO₂ laser tube. This system must be active at all times otherwise the system would burn out within seconds.</li>
<li>Air pump sucking out the exhaust gases from the cutting chamber.</li>
<li>Air compressor blowing the exhaust gases away from the laser beam.</li>
</ol>
<p>With the part being a test piece we decided to disable the compressor for parts of the fill parts.
The remaining dust would block parts of the laser beam and the engraving would become lighter.</p>
<p>However, switching the compressor on and off during the lasering leads to a hanging dust cloud in the camber, which is not removed instantly and the lingering particles spread out leading to random areas receiving less laser light.
You can see a light triangle shape in the top right part:</p>
<p><img alt="rust logo top view" src="https://estada.ch/2021/7/26/experimenting-with-the-new-co2-laser-cutter-at-coredump/2021-07-26_lasercnc/wooden_rust_top.webp"></p>
<p>Also, the lens temperature increases if one uses more energy to cut through material and the compressor must be enabled during these procedures.</p>
<h2>Cutting small Parts</h2>
<p>Fun side effect: Cutting the small circles of the wheel gets very hot and one of the five cuts did set the cutout piece on fire.
Not a problem because small pieces can fall through the grid and do not affect the finished cut out.</p>
<h1>Coredump Blog Series</h1>
<p>Over the coming months the laser cutter blog series will continue.
Read the first two parts today:</p>
<ul>
<li><a href="https://www.coredump.ch/2021/07/15/lasercutter-im-coredump-teil-1-intro/">Lasercutter im Coredump, Teil 1: Intro</a></li>
<li><a href="https://www.coredump.ch/2021/07/25/lasercutter-im-coredump-teil-2-bestellung-und-liefer-odysee/">Lasercutter im Coredump, Teil 2: Bestellung und Liefer-Odysee</a></li>
</ul>
<p>Look out for the next part about how to operate the laser!</p>Salt Box blocks ping and DNS from LAN box2021-07-12T00:00:00+02:002021-07-12T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-07-12:/2021/7/12/salt-box-blocks-ping-and-dns-from-lan-box/<p>Since yesterday I am having a new "fun" problem in my LAN:</p>
<p>Pinging the salt fiber …</p><p>Since yesterday I am having a new "fun" problem in my LAN:</p>
<p>Pinging the salt fiber box too often from the LAN leads to packet loss.
This includes DNS, ICMP and all routed traffic.</p>
<h1>Tests</h1>
<p>Pinging from my box fails after two packages:</p>
<div class="highlight"><pre><span></span><code>PING <span class="m">10</span>.0.0.1 <span class="o">(</span><span class="m">10</span>.0.0.1<span class="o">)</span> <span class="m">56</span><span class="o">(</span><span class="m">84</span><span class="o">)</span> bytes of data.
<span class="m">64</span> Bytes von <span class="m">10</span>.0.0.1: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">1</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">Zeit</span><span class="o">=</span><span class="m">467</span> ms
<span class="m">64</span> Bytes von <span class="m">10</span>.0.0.1: <span class="nv">icmp_seq</span><span class="o">=</span><span class="m">2</span> <span class="nv">ttl</span><span class="o">=</span><span class="m">64</span> <span class="nv">Zeit</span><span class="o">=</span><span class="m">2</span>.91 ms
<span class="o">(</span>--- no more responses ---<span class="o">)</span>
</code></pre></div>
<p>Now pinging other machines beyond the LAN are no longer reachable.</p>
<p>Meanwhile, other machines can ping both, the gateway and others beyond.
Their traffic is not affected in any shape or form.</p>
<p>The salt box appears to have a list of hosts that the box does not agree with.
Since I had to disable the fiber boxes WiFi already after my attempt of <a href="https://estada.ch/2020/5/27/fixing-salt-fiber-box-ap-separation/">fixing the salt fiber box have <strong>failed</strong></a> all my traffic reaches the fiber box over Ethernet.</p>
<h1>Effects</h1>
<p>The affected boxes can no longer reach the admin interface of the salt fiber box.</p>
<p>The <strong>worst part</strong> is: not all traffic is blocked, but most.
That means that browsing the web is broken for the most part (eg. big pictures and videos) but mail still somewhat works. </p>
<p>Some applications like signals can not cope with a faulty link and loose cohesion when ~90% of traffic is dropped.</p>
<p>Since the effects appear limited to the MAC & IP address combination, I temporarely solved it by changing my MAC address and receiving a new dynamic IP.</p>
<p>Others like SSH or protocols tunneling over it like git or socks5 get very slow and sometimes fail to establish a handshake.</p>
<h1>Current configuration</h1>
<p>I hope salt will fix this issue in the future with a firmware fix:</p>
<div class="highlight"><pre><span></span><code>Model Name: Salt_Fiber_Box
Firmware Version: v1.01.38 build105
Boot Code Version: 0.00.01
Hardware Version: R01B
</code></pre></div>Ich war zu Gast beim Akronymisierbar Podcast 🎉2021-05-16T00:00:00+02:002021-05-16T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-05-16:/2021/5/16/ich-war-zu-gast-beim-akronymisierbar-podcast/de/<p>Ich war wieder zu Gast bei <a href="https://twitter.com/hoodie_de">Hendrik</a> und <a href="https://twitter.com/kiliankoe">Kilian</a>. Viel Spass!</p>
<h1>049 - Survivorship Bias</h1>
<p><a href="https://akronymisier.bar/episodes/049"><img alt="Akronymisierbar 049 - Survivorship Bias" src="https://estada.ch/images/akronymisierbar.jpg"></a></p>
<p>Höhrt hier …</p><p>Ich war wieder zu Gast bei <a href="https://twitter.com/hoodie_de">Hendrik</a> und <a href="https://twitter.com/kiliankoe">Kilian</a>. Viel Spass!</p>
<h1>049 - Survivorship Bias</h1>
<p><a href="https://akronymisier.bar/episodes/049"><img alt="Akronymisierbar 049 - Survivorship Bias" src="https://estada.ch/images/akronymisierbar.jpg"></a></p>
<p>Höhrt hier die neuste Folge: <a href="https://akronymisier.bar/episodes/049">https://akronymisier.bar/episodes/049</a></p>
<h1>Holiday Special - Online Konferenzen 2020</h1>
<p><a href="https://akronymisier.bar/episodes/045"><img alt="Akronymisierbar Holiday Special - Online Konferenzen 2020" src="https://estada.ch/images/akronymisierbar.jpg"></a></p>
<p>Höhrt hier die Folge mit Florian <a href="https://twitter.com/Argorak">@Argorak</a>, Jan-Erik <a href="https://twitter.com/badboy_">@badboy_</a> und mir <a href="https://twitter.com/dns2utf8">@dns2utf8</a>: <a href="https://akronymisier.bar/episodes/045">https://akronymisier.bar/episodes/045</a></p>Unable to start Inkscape on Windows 102021-04-06T00:00:00+02:002021-04-06T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-04-06:/2021/4/6/unable-to-start-inkscape-on-windows-10/<p>Today, I wanted to use <a href="https://inkscape.org/">inkscape</a> on a Windows 10 Pro 2004 machine for a quick …</p><p>Today, I wanted to use <a href="https://inkscape.org/">inkscape</a> on a Windows 10 Pro 2004 machine for a quick logo sketch.
For some reason, it just would not start.</p>
<p>So standard procedure:</p>
<ol>
<li>repair app → no effect</li>
<li>reset app data → no effect</li>
<li>uninstall & re-install → no effect</li>
</ol>
<p>The weird thing was, there was always a process running in task manager but no application window visible.</p>
<p><strong>Update:</strong> I opened <a href="https://gitlab.com/inkscape/inbox/-/issues/4715">an Inkscape issue</a></p>
<h1>Windows Store Updates</h1>
<p>I checked the Windows Store and there was no pending update.
During the re-install I noticed a login dialog for the microsoft store.</p>
<p>The store did install inkscape but it was hanging again.</p>
<h1>Windows Store forces Login for app usage</h1>
<p>Then it hit me:
The inkscape window will be hidden until one completes the login into the microsoft store.</p>
<p>For one, it feels a bit like extortion:
You get to use your free apps after you login to microsoft.</p>
<p>Also, a normal user has no chance to realise this.</p>
<ul>
<li>Since the app is started in the background it consumes resources.</li>
<li>After the succsessful login the app just pops open, if the app is started more than once all the instances of all the suppressed windows pop up at the same time</li>
</ul>
<p>Personally, this behaviour is very annoying and discourages me from using the windows store.</p>
<h1>Why use a repo (the store) at all?</h1>
<p>Because it makes updating a machine much simpler.
Linux and Unix distributions have been working like this for over 30 years now.</p>
<p>On a modern Linux system you search the standard software with <a href="https://wiki.debian.org/Apt">apt</a>, <a href="https://fedoraproject.org/wiki/DNF">dnf (former yum)</a>, <a href="https://wiki.archlinux.org/index.php/Pacman">pacman</a> or whatever your disto uses.
Even commercial software one has to buy is mostly shipped with these packet managers for the same reasons.</p>
<p>Today with the need for more isolation, containers with <a href="https://wiki.archlinux.org/index.php/Flatpak">flatpak</a> for desktop applications and <a href="https://wiki.archlinux.org/index.php/Docker">docker</a> for daemons (services) are very common.
These systems call the repo registry, but it works very similarly.</p>Blog Bugs - not migrating to Zola (for now)2021-03-28T00:00:00+01:002021-03-28T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-03-28:/2021/3/28/blog-bugs-not-migrating-to-zola-for-now/<p>In the last week I experienced the limits of the <a href="https://blog.getpelican.com/">pelican blog software</a>.
My workflow includes …</p><p>In the last week I experienced the limits of the <a href="https://blog.getpelican.com/">pelican blog software</a>.
My workflow includes saving work between steps in git.
However, when I switch machines I have to push these commits.</p>
<p>The result is, that unfinished articles end up on the public page.
For that I am sorry.</p>
<p>This weekend I looked into <a href="https://www.getzola.org/">Zola</a> as a replacement.
It has some very compelling features:</p>
<ul>
<li>Has an option to render drafts locally, this would solve my problem.</li>
<li>Live Reload, very comfortable.</li>
<li>It is faster, but since pelican takes ~2 seconds to render the whole page I am okay with this kind of speed.</li>
</ul>
<p>However, it is very hard to keep the dates visible in the URLs.
So <code>/2021/3/28/blog-bugs/</code> would just be <code>/blog-bugs/</code> or <code>/posts/blog-bugs/</code>.</p>
<p>There is a workaround for that by placing <code>_index.md</code> files in every folder:</p>
<div class="highlight"><pre><span></span><code><span class="err">+++</span><span class="w"></span>
<span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"2018"</span><span class="w"></span>
<span class="n">sort_by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"date"</span><span class="w"></span>
<span class="n">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"section.html"</span><span class="w"></span>
<span class="n">page_template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"page.html"</span><span class="w"></span>
<span class="n">transparent</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="w"></span>
<span class="err">+++</span><span class="w"></span>
</code></pre></div>
<p>My blockers for using Zola are:</p>
<ul>
<li>Transparent option should cascade down so I don't have to specify it on every level</li>
<li>More compatible integration for Categories and Tags</li>
</ul>
<p>In the end I would have to make the theme work with the new generator again, but that is just work that has to be done.</p>
<h1>Pelican to Zola Migration Script</h1>
<p>For completeness, I wrote this script to migrate most of my content.
If this helps you, please let me know.</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python3</span>
<span class="c1"># LICENSE: GPLv3 with attribution</span>
<span class="kn">import</span> <span class="nn">glob</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>
<span class="kn">from</span> <span class="nn">dateutil</span> <span class="kn">import</span> <span class="n">parser</span> <span class="k">as</span> <span class="n">timeparser</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="s1">'../estada.ch/content/20*'</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">is_dir</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"is dir, aborting"</span><span class="p">)</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">p</span><span class="o">.</span><span class="n">suffix</span> <span class="o">!=</span> <span class="s2">".md"</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"is not .md, aborting"</span><span class="p">)</span>
<span class="k">break</span>
<span class="n">date</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">stem</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"_"</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">timeparser</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">date</span><span class="p">)</span>
<span class="n">dirname</span> <span class="o">=</span> <span class="s2">"content/</span><span class="si">{}</span><span class="s2">/</span><span class="si">{}</span><span class="s2">/</span><span class="si">{}</span><span class="s2">/"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">day</span><span class="p">)</span>
<span class="n">name</span> <span class="o">=</span> <span class="s2">"content/</span><span class="si">{}</span><span class="s2">/</span><span class="si">{}</span><span class="s2">/</span><span class="si">{}</span><span class="s2">/</span><span class="si">{}{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">day</span><span class="p">,</span> <span class="n">p</span><span class="o">.</span><span class="n">stem</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"_"</span><span class="p">)[</span><span class="mi">1</span><span class="p">],</span> <span class="n">p</span><span class="o">.</span><span class="n">suffix</span><span class="p">)</span>
<span class="n">os</span><span class="o">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">dirname</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">as</span> <span class="n">r</span><span class="p">:</span>
<span class="n">lines</span> <span class="o">=</span> <span class="n">r</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
<span class="n">in_header</span> <span class="o">=</span> <span class="kc">True</span>
<span class="n">content</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span>
<span class="s2">"date"</span><span class="p">:</span> <span class="s2">"</span><span class="si">{}</span><span class="s2">-</span><span class="si">{:02d}</span><span class="s2">-</span><span class="si">{:02d}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">year</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">month</span><span class="p">,</span> <span class="n">d</span><span class="o">.</span><span class="n">day</span><span class="p">),</span>
<span class="p">}</span>
<span class="n">taxonomy</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
<span class="k">if</span> <span class="n">in_header</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">if</span> <span class="n">line</span> <span class="o">==</span> <span class="s2">""</span><span class="p">:</span>
<span class="n">in_header</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">" line: </span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">line</span><span class="p">))</span>
<span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">':'</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="s2">"Tags"</span><span class="p">:</span>
<span class="n">taxonomy</span><span class="p">[</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">()]</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">t</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">strip</span><span class="p">(),</span> <span class="n">val</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">','</span><span class="p">)))</span>
<span class="k">elif</span> <span class="n">key</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"Date"</span><span class="p">,</span> <span class="s2">"Lang"</span><span class="p">,</span> <span class="s2">"SocialImage"</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">key</span> <span class="o">==</span> <span class="s2">"Summary"</span><span class="p">:</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">"description"</span>
<span class="n">headers</span><span class="p">[</span><span class="n">key</span><span class="o">.</span><span class="n">lower</span><span class="p">()]</span> <span class="o">=</span> <span class="n">val</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">content</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
<span class="c1">## make header</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">w</span><span class="p">:</span>
<span class="n">headers</span><span class="p">[</span><span class="s1">'slug'</span><span class="p">]</span> <span class="o">=</span> <span class="n">headers</span><span class="p">[</span><span class="s2">"title"</span><span class="p">]</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"+++</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">headers</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"status"</span><span class="p">,</span> <span class="s2">"category"</span><span class="p">]:</span>
<span class="k">continue</span>
<span class="k">elif</span> <span class="n">k</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"title"</span><span class="p">,</span> <span class="s2">"description"</span><span class="p">,</span> <span class="s2">"slug"</span><span class="p">,</span> <span class="s2">"path"</span><span class="p">,</span> <span class="s2">"template"</span><span class="p">]:</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1"> = "</span><span class="si">{}</span><span class="s1">"</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">))</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1"> = </span><span class="si">{}</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">))</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"[taxonomy]</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">vs</span> <span class="ow">in</span> <span class="n">taxonomy</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="n">s</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vs</span><span class="p">:</span>
<span class="k">if</span> <span class="n">s</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">s</span> <span class="o">+=</span> <span class="s2">", "</span>
<span class="n">s</span> <span class="o">+=</span> <span class="s1">'"</span><span class="si">{}</span><span class="s1">"'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">v</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="si">{}</span><span class="s1"> = [</span><span class="si">{}</span><span class="s1">]</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">s</span><span class="p">))</span>
<span class="n">w</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"+++</span><span class="se">\n\n</span><span class="s2">"</span><span class="p">)</span>
<span class="n">w</span><span class="o">.</span><span class="n">writelines</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</code></pre></div>CSP report collector2021-02-22T00:00:00+01:002021-02-22T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-02-22:/2021/2/22/csp-report-collector/<p><img alt="Admin Interface" src="https://estada.ch/2021/2/22/csp-report-collector/2021-02-22_csp-collector-update/Admin_Interface.png"></p>
<p>Today, I finished the rollout of the new version of my CSP report collector.
It collects …</p><p><img alt="Admin Interface" src="https://estada.ch/2021/2/22/csp-report-collector/2021-02-22_csp-collector-update/Admin_Interface.png"></p>
<p>Today, I finished the rollout of the new version of my CSP report collector.
It collects <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy">Content Security Policy</a> violations that the browsers register when the users interact with the service. </p>
<p>I started this project in 2019 after I repeatedly missed some pages of my setups.
Since I used it on my static blog and on single page installations.
There was no inherit value in the URLs and by not storing the IP addresses of the clients there was no data to be exploited.</p>
<h1>New Features</h1>
<p>After chatting with my friends of <a href="https://www.coredump.ch/">the Coredump.ch Hackerspace</a> I agreed: for setups that have private information in the <a href="https://en.wikipedia.org/wiki/URL">URL</a> this setup was not ideal.
So what is the minimal amount of data that we need?</p>
<ul>
<li>Users</li>
<li>Customers</li>
<li>Users to Customers mapping</li>
</ul>
<p>That leads us to the database relations:</p>
<div class="highlight"><pre><span></span><code>Users <--- Users_Customers ---> Customers <--- Reports
</code></pre></div>
<p>I am a bit proud that I managed to complete the authentication without password or cookies.
This allows me or my customers to use the service as a micro service down the line.</p>
<h1>uBlock Origin: no reports from my own browser?</h1>
<p>uBlock Origin on Firefox changed the default behaviour and <a href="https://github.com/gorhill/uBlock/wiki/Dashboard:-Settings#block-csp-reports">blocks CSP reports by default now</a>.</p>
<p>You can make an exception for your own domains without loosing the protection on the other sites:</p>
<ol>
<li>Open the settings of uBlock Origin</li>
<li>Go to the <code>My rules</code> tab</li>
<li>Unter <code>Temporary Rules</code>: Add an exception for the domains that create the CSP Report<ol>
<li>By adding <code>no-csp-reports: estada.ch false</code> you also include the <a href="https://en.wikipedia.org/wiki/Subdomain">Sub-Domains</a></li>
</ol>
</li>
<li>Click the <code>Save permanently</code> button to persist the changes</li>
<li>Trigger a report to verify that the exception works</li>
</ol>
<p>For me the settings look like this:</p>
<p><img alt="uBlock Origin Rules" src="https://estada.ch/2021/2/22/csp-report-collector/2021-02-22_csp-collector-update/uBlock_Rules.png"></p>
<h1>I would like to use your service</h1>
<p>First, thank you!
I am currently offering this service to my friends on a ask nicely basis and my customers as a part of the whole package.
So message me and we will work something out ☺</p>
<p>You can <a href="/support-my-work/">support my work in general here</a>.</p>Geforce NOW error code 0x000000972021-02-16T00:00:00+01:002021-02-16T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-02-16:/2021/2/16/geforce-now-error-code-0x00000097/<p>There are many error codes, sadly with no official documentation.</p>
<p>The 0x00000097 appears to be a …</p><p>There are many error codes, sadly with no official documentation.</p>
<p>The 0x00000097 appears to be a connection error.
This can happen if</p>
<ul>
<li>the connection actually drops.</li>
<li>you have more than one connection, for example WiFi and Wire at the same time.</li>
</ul>
<p>To fix this, simply disconnect your slower connections.
That is usually WiFi/WLan first, than VPN second. </p>One Factor Authentication - Rust Hungary2021-02-04T00:00:00+01:002021-02-17T12:12:12+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-02-04:/2021/2/4/one-factor-authentication-rust-hungary/<p><a href="https://www.youtube.com/watch?v=s5zfGWKK37w"><img alt="Recording" src="https://estada.ch/2021/2/4/one-factor-authentication-rust-hungary/2021-02-04_hungary-talk.png"></a></p>
<p>I uploaded the source code to <a href="https://gitlab.com/dns2utf8/one_fa_auth">gitlab.com/dns2utf8/one_fa_auth</a>.</p>
<p>Enjoy the recording and have a …</p><p><a href="https://www.youtube.com/watch?v=s5zfGWKK37w"><img alt="Recording" src="https://estada.ch/2021/2/4/one-factor-authentication-rust-hungary/2021-02-04_hungary-talk.png"></a></p>
<p>I uploaded the source code to <a href="https://gitlab.com/dns2utf8/one_fa_auth">gitlab.com/dns2utf8/one_fa_auth</a>.</p>
<p>Enjoy the recording and have a nice day!</p>Experimenting with rendering a Table of Contents2021-01-09T00:00:00+01:002021-01-09T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2021-01-09:/2021/1/9/experimenting-with-rendering-a-table-of-contents/<p>Today I implemented a feature I was thinking about: A ToC on the top of all …</p><p>Today I implemented a feature I was thinking about: A ToC on the top of all the articles.</p>
<p>Originally, my idea was to have <a href="https://blog.getpelican.com/">pelican</a> generate it statically.
That would enable the ToC for subscribers of the <a href="/feeds/all.atom.xml">Atom News Feed</a> too.
But it was harder than including a piece of JavaScript in the HTML template and some feed readers might already show their own ToC.</p>
<p>So now let's see how I went about it and create a lot of headings in the process so the generator creates a lot of output.</p>
<h1>My quick development process</h1>
<p>I want to see the first three levels </p>
<h2>Level based approach</h2>
<p>My first idea was to have </p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">toc_root</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">level</span><span class="o">:</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="nx">children</span><span class="o">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> </span><span class="nx">parent</span><span class="o">:</span><span class="w"> </span><span class="kc">undefined</span><span class="p">,</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
</code></pre></div>
<h3>Pros</h3>
<ul>
<li>Would be able to detect the highest level</li>
<li>Very long titles would wrap correctly</li>
</ul>
<h3>Cons</h3>
<ul>
<li>Hard to implement</li>
<li>Coming back to JavaScript from <a href="https://rust-lang.org/">Rust</a> knowing what is a copy vs. what is a borrow/reference is hard and lead me down a very strange path</li>
</ul>
<h2>CSS based approach</h2>
<h3>Pros</h3>
<ul>
<li>Simple to implement</li>
</ul>
<h3>Cons</h3>
<ul>
<li>Long titles could give a reader the wrong impression of the level</li>
</ul>
<h4>The Code</h4>
<p>This section will now show up in the ToC:</p>
<h5>JavaScript</h5>
<div class="highlight"><pre><span></span><code><span class="p">(</span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">//! Render a TOC on the top of Blog Articles</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">is_article</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">entry_content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s2">"div.entry-content"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">toc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">''</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">let</span><span class="w"> </span><span class="nx">heading</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="nx">entry_content</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">"h1,h2,h3"</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">id</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="mf">1</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">heading</span><span class="p">.</span><span class="nx">innerText</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">id_string</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`</span><span class="si">${</span><span class="nx">id</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">depth</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">heading</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">substr</span><span class="p">(</span><span class="mf">1</span><span class="p">));</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">"a"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">a</span><span class="p">.</span><span class="nx">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">id_string</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="nx">heading</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">toc</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="sb">` <a class="toc_level</span><span class="si">${</span><span class="nx">depth</span><span class="si">}</span><span class="sb">" href="#</span><span class="si">${</span><span class="nx">id_string</span><span class="si">}</span><span class="sb">"></span><span class="si">${</span><span class="nx">title</span><span class="si">}</span><span class="sb"></a>\n`</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">id</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nx">toc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sb">`<div class="toc_frame">\n <h1>Table of Contents:</h1>\n <div class="toc_entries">\n</span><span class="si">${</span><span class="nx">toc</span><span class="si">}</span><span class="sb"> </div>\n</div>`</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="nx">entry_content</span><span class="p">.</span><span class="nx">innerHTML</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">toc</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">entry_content</span><span class="p">.</span><span class="nx">innerHTML</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">function</span><span class="w"> </span><span class="nx">is_article</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">parts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">location</span><span class="p">.</span><span class="nx">pathname</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">"/"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">first</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">parts</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="s1">''</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">first</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="mf">4</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="kd">const</span><span class="w"> </span><span class="nx">year</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">first</span><span class="p">,</span><span class="w"> </span><span class="mf">10</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">year</span><span class="w"> </span><span class="o">></span><span class="w"> </span><span class="mf">2000</span><span class="w"> </span><span class="o">&&</span><span class="w"> </span><span class="nx">year</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="mf">2100</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">})()</span><span class="w"></span>
</code></pre></div>
<h5>CSS</h5>
<div class="highlight"><pre><span></span><code><span class="p">.</span><span class="nc">toc_frame</span><span class="w"> </span><span class="nt">h1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-top</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mf">1.2</span><span class="kt">rem</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">toc_frame</span><span class="w"> </span><span class="p">.</span><span class="nc">toc_entries</span><span class="w"> </span><span class="nt">a</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="kc">block</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">toc_frame</span><span class="w"> </span><span class="p">.</span><span class="nc">toc_entries</span><span class="w"> </span><span class="nt">a</span><span class="p">.</span><span class="nc">toc_level1</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-left</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">toc_frame</span><span class="w"> </span><span class="p">.</span><span class="nc">toc_entries</span><span class="w"> </span><span class="nt">a</span><span class="p">.</span><span class="nc">toc_level2</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-left</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">.</span><span class="nc">toc_frame</span><span class="w"> </span><span class="p">.</span><span class="nc">toc_entries</span><span class="w"> </span><span class="nt">a</span><span class="p">.</span><span class="nc">toc_level3</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">margin-left</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="kt">em</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
</code></pre></div>
<h1>Test headings</h1>
<h2>Test h2</h2>
<h3>Test h3</h3>
<h3>Test h3</h3>
<h2>Test h2</h2>
<h4>Test h4</h4>Unable to start KVM virtual machines with libvirt and AppArmor2020-12-21T00:00:00+01:002020-12-21T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-12-21:/2020/12/21/unable-to-start-kvm-virtual-machines-with-libvirt-and-apparmor/<p>Today, I had a weird error. After a reboot without any configuration changes some of the …</p><p>Today, I had a weird error. After a reboot without any configuration changes some of the VMs would not start anymore.</p>
<p><code>virsh list --all</code> would still report the machines, but starting them is no longer possible.</p>
<div class="highlight"><pre><span></span><code><span class="c1"># virsh start ${NAME}</span>
error: Failed to start domain <span class="si">${</span><span class="nv">NAME</span><span class="si">}</span>
error: internal error: Process exited prior to exec: libvirt: error : unable to <span class="nb">set</span> AppArmor profile <span class="s1">'libvirt-abcdef01-2345-6789-0abc-def012345678'</span> <span class="k">for</span> <span class="s1">'/usr/bin/kvm-spice'</span>: No such file or directory
</code></pre></div>
<h1>Solving the problem</h1>
<p>There appears to be a bug in <a href="https://libvirt.org/">libvirt</a>.
If there is a server or daemon crash during the start or creation of the VMs, the generated AppArmor profiles sometimes are empty.</p>
<p>When we restart the system later libvirt tries to reuse the generated policy and fails if the file is empty.
So we have to check for these files:</p>
<div class="highlight"><pre><span></span><code>sudo -i <span class="c1"># become root</span>
<span class="nb">cd</span> /etc/apparmor.d/libvirt
ls -asl
</code></pre></div>
<p>The listing should look something like this:</p>
<div class="highlight"><pre><span></span><code><span class="m">4</span> drwxr-xr-x <span class="m">2</span> root root <span class="m">4096</span> Dec <span class="m">21</span> <span class="m">19</span>:41 .
<span class="m">4</span> drwxr-xr-x <span class="m">10</span> root root <span class="m">4096</span> May <span class="m">22</span> <span class="m">2020</span> ..
<span class="m">0</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">0</span> Dec <span class="m">21</span> <span class="m">18</span>:52 libvirt-abcdef01-2345-6789-0abc-def012345678
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">430</span> Dec <span class="m">21</span> <span class="m">19</span>:41 libvirt-abcdef01-2345-6789-0abc-def012345678.files
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">293</span> Dec <span class="m">21</span> <span class="m">19</span>:41 libvirt-abcdef01-2345-6789-0abc-def012345678
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">739</span> Dec <span class="m">21</span> <span class="m">19</span>:41 libvirt-abcdef01-2345-6789-0abc-def012345678.files
<span class="m">0</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">0</span> Dec <span class="m">21</span> <span class="m">18</span>:57 libvirt-abcdef01-2345-6789-0abc-def012345678
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">499</span> Dec <span class="m">21</span> <span class="m">19</span>:45 libvirt-abcdef01-2345-6789-0abc-def012345678.files
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">265</span> Aug <span class="m">10</span> <span class="m">2016</span> libvirt-abcdef01-2345-6789-0abc-def012345678
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">499</span> Feb <span class="m">8</span> <span class="m">2018</span> libvirt-abcdef01-2345-6789-0abc-def012345678.files
...
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">342</span> Aug <span class="m">28</span> <span class="m">2018</span> TEMPLATE.lxc
<span class="m">4</span> -rw-r--r-- <span class="m">1</span> root root <span class="m">192</span> Aug <span class="m">28</span> <span class="m">2018</span> TEMPLATE.qemu
</code></pre></div>
<p>We can see here that there are some newer and some older empty files.
The next step is to simply delete the files like this:</p>
<div class="highlight"><pre><span></span><code>rm libvirt-abcdef01-2345-6789-0abc-def012345678
</code></pre></div>
<p>Now we are able to start the VMs with <code>virsh</code>, <code>virt-manager</code> or any other interface:</p>
<div class="highlight"><pre><span></span><code>sudo virsh start <span class="si">${</span><span class="nv">NAME</span><span class="si">}</span>
</code></pre></div>
<p>Don't forget to close the root shell by giving it the command <code>exit</code>.</p>
<h2>What you should not do</h2>
<p>There are some people that disable <code>apparmor</code>.
This is a bad idea because it reduces the security of your system.</p>
<p>Make sure that the file <code>/etc/libvirt/qemu.conf</code> either is on the default value or set to <code>apparmor</code> and <code>selinux</code> as a fallback:</p>
<div class="highlight"><pre><span></span><code><span class="c1">#security_driver = "apparmor"</span><span class="w"></span>
<span class="n">security_driver</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s">"apparmor"</span><span class="p">,</span><span class="w"> </span><span class="s">"selinux"</span><span class="w"> </span><span class="p">]</span><span class="w"></span>
</code></pre></div>Atomic Counters and Cache Lines2020-12-17T00:00:00+01:002020-12-21T10:10:10+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-12-17:/2020/12/17/atomic-counters-and-cache-lines/<p><a href="https://youtu.be/bSAIne9wSkQ"><img alt="Recording on YouTube" src="https://estada.ch/2020/12/17/atomic-counters-and-cache-lines/2020-12-17_atomic-counters-and-cachelines.png"></a></p>
<p>I uploaded <a href="https://github.com/dns2utf8/atomics_and_visibility_problem/blob/master/pdf/2020-12-17_cache-lines-at-linz.pdf">the slides</a> and the <a href="https://github.com/dns2utf8/atomics_and_visibility_problem/blob/master/cacheline_aligned_atomics/src/main.rs">demo code</a> to github.
Thank you <a href="https://rust-linz.at/">Rust Meetup Linz</a> for …</p><p><a href="https://youtu.be/bSAIne9wSkQ"><img alt="Recording on YouTube" src="https://estada.ch/2020/12/17/atomic-counters-and-cache-lines/2020-12-17_atomic-counters-and-cachelines.png"></a></p>
<p>I uploaded <a href="https://github.com/dns2utf8/atomics_and_visibility_problem/blob/master/pdf/2020-12-17_cache-lines-at-linz.pdf">the slides</a> and the <a href="https://github.com/dns2utf8/atomics_and_visibility_problem/blob/master/cacheline_aligned_atomics/src/main.rs">demo code</a> to github.
Thank you <a href="https://rust-linz.at/">Rust Meetup Linz</a> for hosting! 😊</p>How to build a multiplayer game - RustFest Global 2020 Pre-Event2020-11-02T00:00:00+01:002020-11-27T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-11-02:/2020/11/2/how-to-build-a-multiplayer-game-rustfest-global-2020-pre-event/<p><a href="https://www.youtube.com/watch?v=Yb-QR3Vm3sk"><img alt="Sketch of one Tick" src="https://estada.ch/2020/11/2/how-to-build-a-multiplayer-game-rustfest-global-2020-pre-event/2020-11-02-mps.estada.ch/MPS Schema.jpeg"></a>
As announced on <a href="https://www.meetup.com/Rust-Zurich/events/274286096/">the rust zürisee meetup</a>: I gave a talk about How to build a …</p><p><a href="https://www.youtube.com/watch?v=Yb-QR3Vm3sk"><img alt="Sketch of one Tick" src="https://estada.ch/2020/11/2/how-to-build-a-multiplayer-game-rustfest-global-2020-pre-event/2020-11-02-mps.estada.ch/MPS Schema.jpeg"></a>
As announced on <a href="https://www.meetup.com/Rust-Zurich/events/274286096/">the rust zürisee meetup</a>: I gave a talk about How to build a multiplayer game with <a href="https://actix.rs/">actix-web</a> that people with any modern browser shipping JavaScript, <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">Canvas Context2D</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket">Websocket</a> can play.</p>
<p>You can <a href="https://www.youtube.com/watch?v=Yb-QR3Vm3sk">watch the recording on Youtube</a> now.</p>
<h1>Play the game</h1>
<p>You can <a href="https://mps.estada.ch">play my MultiPlayer Snake on mps.estada.ch with your browser</a>.</p>
<h1>Challenges during deployments</h1>
<p>While I did plan to finish development before the conference, automating the deployment is always handy because:</p>
<ol>
<li>No more human error</li>
<li>Even in a stressful situation after something has happened, the deployment will working and not introduce new problems</li>
</ol>
<h2>CSP: content-security-policy</h2>
<h3>Quotations are opposite to most programming languages</h3>
<p>There are two important rules:</p>
<ol>
<li>Keywords are in single quotes <code>'</code> <strong>but</strong> Strings are not quoted</li>
<li>Directives are separated by a simple space <code></code> <strong>no colon</strong></li>
</ol>
<h3>Connect-src 'self' does not cover wss://self</h3>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src">In the docs</a> there is a very innocent sentence:</p>
<div class="highlight"><pre><span></span><code>... the same URL scheme and port number.
</code></pre></div>
<p>This means for a connection over HTTPS <code>'self'</code> does not cover <code>wss://</code> even though the WebSocket connection is setup over HTTPS.
However, <code>fetch('/...')</code> for an API is allowed.
Allowing the client to send an error message when it is unable to open the WebSocket.</p>
<p>So the final policy now includes the hostname for the WebSocket and the frame-ancestors for <a href="https://rustfest.global">RustFest Global</a>:</p>
<div class="highlight"><pre><span></span><code>default-src <span class="s1">'none'</span><span class="p">;</span> frame-ancestors https://*.rustfest.global<span class="p">;</span> connect-src <span class="s1">'self'</span> wss://mps.estada.ch<span class="p">;</span> font-src <span class="s1">'self'</span><span class="p">;</span> img-src <span class="s1">'self'</span> data:<span class="p">;</span> script-src <span class="s1">'self'</span><span class="p">;</span> style-src <span class="s1">'self'</span>
</code></pre></div>
<h2>Deployment from Arch to Debian</h2>
<p>Using <a href="https://www.archlinux.org/">Arch Linux</a> is very nice for development, because like <a href="https://gentoo.org/">Gentoo</a> it ships with all the latest software but unlike Gentoo you don't have to spend hours compiling everything from source.
To me this compromise means:
* I can use the latest compilers within a week of their release without having to check all their announcements
* My software is automatically future prove as the other distros will update to this feature set within the next 6 to 24 months
* The setup is rather minimal because I have to install everything myself</p>
<p>On the other hand, I operate servers that I don't want to update all the time and prefere more stable software.
The perfect match for me since 2006 is <a href="https://www.debian.org">Debian</a> and with <a href="https://systemd.io/">systemd</a> I get a <a href="https://gitlab.com/dns2utf8/multiplayer_snake/-/blob/main/mps.service">service watchdog</a> for free.</p>
<p>However, after transferring the compiled project to the host reveals problem:</p>
<div class="highlight"><pre><span></span><code>./multiplayer_snake: /lib/x86_64-linux-gnu/libc.so.6: version <span class="sb">`</span>GLIBC_2.32<span class="err">'</span> not found <span class="o">(</span>required by ./multiplayer_snake<span class="o">)</span>
</code></pre></div>
<h3>Building with Docker</h3>
<p>Cross-Compiling for an older libc is currently not that simple.
So building in a <a href="https://en.wikipedia.org/wiki/Virtual_machine">VM</a> or a container is much more comfortable.</p>
<p>I would love to switch to the <a href="https://podman.io/">root-less podman</a>, but if you have to use docker:
Don't forget to pass the <code>--user</code> flags or run the command with <a href="https://wiki.archlinux.org/index.php/Sudo"><code>sudo</code></a>:</p>
<div class="highlight"><pre><span></span><code>docker run --rm --user <span class="s2">"</span><span class="k">$(</span>id -u<span class="k">)</span><span class="s2">"</span>:<span class="s2">"</span><span class="k">$(</span>id -g<span class="k">)</span><span class="s2">"</span> -v <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">"</span>:/usr/src/myapp -w /usr/src/myapp rust:latest cargo build --release
</code></pre></div>
<p>Don't forget to pull <code>rust:latest</code> after every release of <a href="https://blog.rust-lang.org/">the rust compiler</a> with:</p>
<div class="highlight"><pre><span></span><code>docker pull rust:latest
</code></pre></div>No password login concept2020-10-12T00:00:00+02:002020-10-12T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-10-12:/2020/10/12/no-password-login-concept/<p>Passphrases are long and hard to remember.
We still need an email address and sometimes also …</p><p>Passphrases are long and hard to remember.
We still need an email address and sometimes also a second factor. But how much security do we actually gain with this?
What if we used a different system that would put less burden on the users mind?</p>
<h1>(Very short) Historic context of logins</h1>
<p>The evolution of it security and authentication started before I was born.
On the big picture it started out like this:</p>
<ol>
<li><strong>Physical security only:</strong> There was no internet, if you could walk into the room with the machine, that was it. Connecting two systems was done by installing a cable</li>
<li><strong>Rarity:</strong> It was so expensive to get a computer that those few that had access to such a machine for the most part had better things to do. First multi user systems had accounts for organisational purposes. Plaintext passwords, no <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS (former SSL)</a> or <a href="https://en.wikipedia.org/wiki/Ssh_(Secure_Shell)">SSH</a></li>
<li><strong>IP based security:</strong> IP won the protocol war over Apple Talk and others. The first <a href="https://en.wikipedia.org/wiki/Network_File_System">NFS</a> version with any sort of security was created with IPs/Subnets per export (aka. per shared foler)</li>
<li><strong>User & Password with groups:</strong> With HTTP 1.0 the need for sessions arose, because now users started moving between machines and the first laptops appeared and now machines could move between networks too. <em>Anonymous accounts</em> are in their prime. Secret services blossom and infiltrate many systems and snoop on many cables and internet exchanges</li>
<li><strong>Client certificates:</strong> SSH public key authentication, signed eMail, ...</li>
<li><strong>API keys:</strong> With machine to machine communication increasing handling just one field is simpler than two plus it is simpler to enforce minimal length </li>
<li><strong>Password managers:</strong> longer passwords, need to share lots of passwords in a team</li>
<li><strong>Single Sign On (SSO):</strong> centralising authentication or outsourcing it to another provider. Simplifies services but creates new dependencies and risks</li>
<li><strong>Second factor (2FA):</strong> SIM cards, SmartCards, RSA number tokens, One-Time-Pads (lists of random numbers one has to cross out on each access, very easy to disrupt service with desync), SMS token, challenge response </li>
<li><strong>Pass-Phrases:</strong> After years of hacked accounts because people choose <code>password</code> or <code>0000</code> as their secret there is a shift to longer secrets. Remembering a sentence as a password is about as hard as a complicated, long password but gives much more it security</li>
</ol>
<p>Nowadays on of the biggest problems with pass phrases is that they get reused over many services.
So obtaining one username/password or email/password pair has a high chance to enable access to multiple services.</p>
<h1>An email and TOTP authentication scheme</h1>
<p>I propose a new schema where the user uses a combination of their email address and and <a href="https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm">TOTP</a> token to login.</p>
<h2>Why?</h2>
<ul>
<li>No additional network connection required</li>
<li>Protects against keyloggers</li>
<li>Follows the principal of <a href="https://www.martinfowler.com/bliki/Datensparsamkeit.html">Datensparsamkeit</a></li>
<li>Users can use one application eg. FreeOTP Authenticator (<a href="https://play.google.com/store/apps/details?id=org.fedorahosted.freeotp">Android</a>/<a href="https://apps.apple.com/us/app/freeotp-authenticator/id872559395">Apple</a>) from Red Hat for multiple services on the same device<ul>
<li>There are TOTP clients for desktop users too</li>
</ul>
</li>
</ul>
<h2>Why not?</h2>
<p>It still requires people to either have a smart phone or some experience with computers.</p>
<p>This can be mitigated by making the page that displays the QR-Code an already signed in page, then the user must login by receiving an email and confirming their address every time.</p>
<h2>Specification: What is the security scheme supposed to do?</h2>
<h3>Registration / Signup</h3>
<p>Is done by sending the user an email with a link to a confirm page showing the TOTP QR-Code.</p>
<h3>TOTP secret lost</h3>
<p>In case the user looses their device, then they can request a new secret via their email account.</p>
<p>To prevent abusive users this new secret must not be applied without the user using it.</p>
<p>Also, applying a rate limit (eg. once per 30min) would improve matter further so the user is not flooded with emails.</p>
<h2>Implementation: How does it work?</h2>
<p>I propose the following fields in your user structure:</p>
<div class="highlight"><pre><span></span><code><span class="k">struct</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">// ... other fields</span>
<span class="w"> </span><span class="n">email</span>: <span class="nb">String</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">active_totp</span>: <span class="nc">TotpSecret</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="sd">/// If this is `Some(secret)` we will replace the</span>
<span class="w"> </span><span class="sd">/// active secret with this one the first time </span>
<span class="w"> </span><span class="sd">/// it is used to complete intentional reset</span>
<span class="w"> </span><span class="sd">/// while preventing a DoS attack and then set</span>
<span class="w"> </span><span class="sd">/// this to `None`</span>
<span class="w"> </span><span class="n">next_totp</span>: <span class="nb">Option</span><span class="o"><</span><span class="n">TotpSecret</span><span class="o">></span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h3>Registration / Signup</h3>
<ol>
<li>The signup form must contain at least their email address</li>
<li>The user clicks on a confirm link leading to a page showing a QR code of the TOTP secret that the user scans with their app (Computer or SmartPhone)</li>
<li>The user generates their first TODP code to complete the registration and do their first login</li>
</ol>
<h3>Login</h3>
<ol>
<li>The user enters their email address and the current TOTP code from their application</li>
</ol>
<p>That's it, no async messages or challenge response needed.</p>
<h2>Assurance: Does it really work?</h2>
<p>As with many other services the users security lies with the eMail account.</p>
<p>Not storing pass-phrase-hashes in your own database is something that can not be stolen and there is no need for dealing with <a href="https://en.wikipedia.org/wiki/Salt_(cryptography)">hash salts</a>.</p>
<h2>Demo Implementations</h2>
<p>I am building a working prototype, stay tuned.</p>
<p>If you have a catchy name, let me know: <a href="https://twitter.com/dns2utf8">@dns2utf8</a></p>
<h1>Alternative concepts</h1>
<h2>EMail only - no persistent password</h2>
<ul>
<li>You need an email address to create an account.</li>
<li>For every login you get a "reset email" with a temporary password, that is valid for a short time (eg. 10 or 30 minutes).</li>
</ul>
<p>I like this concept because a lot of people work like this anyways since they keep forgetting their password and reset it all the time already.</p>
<h2>Companion app</h2>
<ul>
<li>You must have a smart phone with a recent firmware (last two years) - used to be a specific brand eg. iPhone only</li>
<li>After Login/Password you must press a button in the app</li>
</ul>
<p>I dislike this concept because it excludes a range of people (old, poor, rural, colour blind, injured, ...) and uses up a lot of space on mobile phones.</p>
<p>Another huge minus is that these apps track the use outside of the normal usage.
There are a number of companies out there that try to collect as much data as possible on all users and then try to sell it later.
Why is this a problem here?</p>
<p>Banks and Insurance companies can use their connection logs to guess the mental health state of their clients.
With an app on the device they get a connection every time the device wakes up, which is strongly correlated with the users usage pattern since most people don't use their devices while they are asleep.</p>Use my Heise Single Page Add-on with firefox nightly2020-10-03T00:00:00+02:002020-10-03T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-10-03:/2020/10/3/use-my-heise-single-page-add-on-with-firefox-nightly/<p>If you would like to use my <a href="https://addons.mozilla.org/en-US/firefox/addon/heise-single-page/">Heise Single Page extension</a> to enhance readablilty of the …</p><p>If you would like to use my <a href="https://addons.mozilla.org/en-US/firefox/addon/heise-single-page/">Heise Single Page extension</a> to enhance readablilty of the news site.
Now there is a workaround for Android after mozilla decided to stop allowing any addons on the android version of firefox:</p>
<ol>
<li>Install Firefox nightly: <a href="https://play.google.com/store/apps/details?id=org.mozilla.fenix">https://play.google.com/store/apps/details?id=org.mozilla.fenix</a></li>
<li>Enable Debug Menu in Firefox nightly:<ol>
<li>Open Settings</li>
<li>Scroll down to the end, click "About"</li>
<li>Tap the logo at least 10 times until you see a toast message announcing "Debug menu enables"</li>
<li>Go back one level</li>
</ol>
</li>
<li>In the <code>extended</code> section select <code>Custom Add-on collection</code>:</li>
<li>Enter these details:<ol>
<li>User ID: <code>14027677</code></li>
<li>Collection name: <code>AllMyAddons</code></li>
<li><a href="./2020-10-03_addons-firefox-nightly/collection_settings.png"><img alt="Collection Settings" src="https://estada.ch/2020/10/3/use-my-heise-single-page-add-on-with-firefox-nightly/2020-10-03_addons-firefox-nightly/collection_settings.png"></a></li>
</ol>
</li>
<li>Go back and select the add-on from the proposed list<ol>
<li><a href="./2020-10-03_addons-firefox-nightly/extension_list.png"><img alt="Extension List" src="https://estada.ch/2020/10/3/use-my-heise-single-page-add-on-with-firefox-nightly/2020-10-03_addons-firefox-nightly/extension_list.png"></a></li>
</ol>
</li>
</ol>
<p>The collection features more than my own addon now, because the collection is not stacking on top of the default one.</p>
<h1>Why do I have to do that?</h1>
<p>Well the truth is, since mozilla dropped general add-ons with firefox 79 in August this is the first and currently only way to use not so popular extensions on android.</p>My Rust 2021 Wishlist for the 2021 Roadmap2020-09-23T00:00:00+02:002020-09-25T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-09-23:/2020/9/23/my-rust-2021-wishlist-for-the-2021-roadmap/<p>The Rust team has put out a <a href="https://blog.rust-lang.org/2020/09/03/Planning-2021-Roadmap.html">call for 2021 Roadmap</a>.
So, I was thinking I …</p><p>The Rust team has put out a <a href="https://blog.rust-lang.org/2020/09/03/Planning-2021-Roadmap.html">call for 2021 Roadmap</a>.
So, I was thinking I would bring up a few things that I would love to see in the next edition of Rust:</p>
<ol>
<li><a href="#adl">Argument dependant lookup in <code>match</code></a></li>
<li><a href="#unwrap_all"><code>unwrap_all()</code></a></li>
<li><a href="#split_join">Coupling <code>split()</code> and <code>join()</code></a></li>
<li><a href="#backwards_compatibility">Backwards compatibility</a></li>
</ol>
<h1>Argument dependant lookup in <code>match</code> <a name="adl"></a></h1>
<p>Consider this code for talking to a secondary processor:</p>
<div class="highlight"><pre><span></span><code><span class="cp">#[derive(Debug)]</span><span class="w"></span>
<span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">SecondaryProcessor</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Opening</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">Writing</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">Reading</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">Closing</span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>When we match on it today, we have to do this:</p>
<div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Ready</span><span class="p">;</span><span class="w"></span>
<span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Opening</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Ready</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Writing</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Reading</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Closing</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>This is a lot of text. What can we do today?
Well we can use <code>use</code>:</p>
<div class="highlight"><pre><span></span><code><span class="k">use</span><span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="o">*</span><span class="p">;</span><span class="w"></span>
<span class="kd">let</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Ready</span><span class="p">;</span><span class="w"></span>
<span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Opening</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Closing</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">others</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>Now we add another connection to the system, for example a network connection and we want the same ergonomics:</p>
<div class="highlight"><pre><span></span><code><span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">TlsServerConnection</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Connecting</span><span class="p">(</span><span class="nb">String</span><span class="p">),</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="p">(</span><span class="n">Socket</span><span class="p">),</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="k">use</span><span class="w"> </span><span class="n">TlsServerConnection</span>::<span class="o">*</span><span class="p">;</span><span class="w"></span>
</code></pre></div>
<p>But now <code>let s = Ready;</code> is no longer clearly defined.</p>
<h2>With ADL-proposal</h2>
<p>Argument dependant lookup would solve that problem inside <code>match</code>, <code>if let</code> and <code>while let</code> nicely.</p>
<div class="highlight"><pre><span></span><code><span class="kd">let</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">SecondaryProcessor</span>::<span class="n">Ready</span><span class="p">;</span><span class="w"></span>
<span class="kd">let</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TlsServerConnection</span>::<span class="n">Ready</span><span class="p">(</span><span class="o">..</span><span class="p">.);</span><span class="w"></span>
<span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Opening</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Closing</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">others</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="k">match</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Connecting</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="p">(</span><span class="n">Socket</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="cm">/* do something */</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">Closing</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="cm">/* do something */</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="k">while</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">Ready</span><span class="p">(</span><span class="n">socket</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">t</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="cm">/* do something */</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h3>Upgrade <code>unreachable_patterns</code> warning to hard error</h3>
<p>Currently <a href="https://play.rust-lang.org/?gist=d7acd42d203a3a583f8d602c041de58f&version=stable&mode=debug&edition=2018">this code</a> only emits a warning:</p>
<div class="highlight"><pre><span></span><code><span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Opening</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"Opening: {}"</span><span class="p">,</span><span class="w"> </span><span class="n">string</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Closing</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"Ready or Closing"</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">others</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span><span class="w"> </span><span class="n">others</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"/* do something */"</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>With this proposal that warning must become a hard error so no ambiguiets rise:</p>
<div class="highlight"><pre><span></span><code>error: unreachable pattern
--> src/main.rs:18:9
<span class="p">|</span>
<span class="m">17</span> <span class="p">|</span> <span class="nv">others</span> <span class="o">=</span>> <span class="o">{</span> println!<span class="o">(</span><span class="s2">"{:?}"</span>, others<span class="o">)</span><span class="p">;</span> <span class="o">}</span>
<span class="p">|</span> ------ matches any value
<span class="m">18</span> <span class="p">|</span> <span class="nv">_</span> <span class="o">=</span>> <span class="o">{</span> println!<span class="o">(</span><span class="s2">"/* do something */"</span><span class="o">)</span><span class="p">;</span> <span class="o">}</span>
<span class="p">|</span> ^ unreachable pattern
<span class="p">|</span>
<span class="o">=</span> note: <span class="sb">`</span><span class="c1">#[error(unreachable_patterns)]` on by default</span>
</code></pre></div>
<h4>Update 3: Allow macro creators to generate too many branches</h4>
<p>A macro could generate code like this:</p>
<div class="highlight"><pre><span></span><code><span class="k">match</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="cp">#[allow(unreachable_patterns)]</span><span class="w"></span>
<span class="w"> </span><span class="n">Opening</span><span class="p">(</span><span class="n">string</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"Opening: {}"</span><span class="p">,</span><span class="w"> </span><span class="n">string</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">Ready</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Closing</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"Ready or Closing"</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">others</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"{:?}"</span><span class="p">,</span><span class="w"> </span><span class="n">others</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"/* catch all from macro */"</span><span class="p">);</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>That would allow the expansion to have a catch all for cases where a macro might not cover all cases but knows that the default is unreachable or unlikely.</p>
<hr>
<h1>Unwrap_all() <a name="unwrap_all"></a></h1>
<p>There are some APIs that return <code>Option<T></code> or <code>Result<T, E></code> and accessing nodes in a tree is usually riddled with these return types.</p>
<p>Querying can result in a type like this: <code>Option<Result<Option<Leaf>, TreeError>></code></p>
<p>So it would be nice to have an <code>{Option,Result}::unwrap_all(self)</code> or a macro <code>unwrap_all!(Option<T>)</code> that can handle both types similar to <a href="https://doc.rust-lang.org/std/option/enum.Option.html#method.flatten"><code>{Option, Result}::flatten(self)</code></a>.</p>
<h3>Update 2: I implemented this partially: <a href="https://crates.io/crates/unwrap_all">checkout unwrap_all on crates.io</a></h3>
<h3>Update: Added reference to flatten and prototype unwrap_all!</h3>
<h2>Considerations</h2>
<p>One problem is that sometimes the use might not want to unwrap all levels, that would lead to code like this:</p>
<div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">unpack_leaf</span><span class="p">(</span><span class="n">result</span>: <span class="nb">Option</span><span class="o"><</span><span class="nb">Option</span><span class="o"><</span><span class="nb">Result</span><span class="o"><</span><span class="n">Leaf</span><span class="p">,</span><span class="w"> </span><span class="n">TreeError</span><span class="o">>>></span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Option</span><span class="o"><</span><span class="n">Leaf</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">unwrap_all</span><span class="p">())</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>That one could be solved by a macro with a depth parameter like this:</p>
<div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">unpack_leaf</span><span class="p">(</span><span class="n">result</span>: <span class="nb">Option</span><span class="o"><</span><span class="nb">Option</span><span class="o"><</span><span class="nb">Option</span><span class="o"><</span><span class="n">Leaf</span><span class="o">>>></span><span class="p">)</span><span class="w"> </span>-> <span class="nb">Option</span><span class="o"><</span><span class="n">Leaf</span><span class="o">></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">unwrap_levels</span><span class="o">!</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="n">result</span><span class="p">)</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<hr>
<h1>Coupling split() and join() <a name="split_join"></a></h1>
<p>One of the big steps forward in my productivity in Rust was when I fully understood <a href="https://doc.rust-lang.org/std/iter/index.html"><code>Iterator</code></a>.</p>
<p>I expected a <code>join()</code> operation over any <code>Iterator</code> such as <code>Split</code>:</p>
<div class="highlight"><pre><span></span><code><span class="fm">println!</span><span class="p">(</span><span class="s">"direct: {}"</span><span class="p">,</span><span class="w"> </span><span class="s">"ab,cd,ed"</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">","</span><span class="p">).</span><span class="n">join</span>::<span class="o"><</span><span class="nb">String</span><span class="o">></span><span class="p">(</span><span class="s">"-"</span><span class="p">));</span><span class="w"></span>
</code></pre></div>
<p>But that is currently not possible.</p>
<h2>Workaround: collect()</h2>
<div class="highlight"><pre><span></span><code><span class="fm">println!</span><span class="p">(</span><span class="s">"collect: {}"</span><span class="p">,</span><span class="w"> </span><span class="s">"ab,cd,ed"</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">","</span><span class="p">).</span><span class="n">collect</span>::<span class="o"><</span><span class="nb">Vec</span><span class="o"><</span><span class="n">_</span><span class="o">>></span><span class="p">().</span><span class="n">join</span><span class="p">(</span><span class="s">"-"</span><span class="p">));</span><span class="w"></span>
</code></pre></div>
<h2>Future plans</h2>
<p>Since this issues has been around, there is a <a href="https://github.com/rust-lang/rust/issues/27747">tracking issue</a>.</p>
<!-- [unstable book](https://doc.rust-lang.org/unstable-book/index.html) -->
<hr>
<h1>Backwards compatibility <a name="backwards_compatibility"></a></h1>
<p>This last one is a little more subtle.
Most people compile their software on the same operating system as they intend to run it on.</p>
<p>However, sometimes one has to support an older version.
As of today the two most popular options are:</p>
<h2>1. Virtualisation or secondary machine.</h2>
<p>Essentially revert </p>
<h2>2. Containers</h2>
<p>This one has gained a lot of popularity, since it solves the problem with very little overhead on most operating systems:</p>
<div class="highlight"><pre><span></span><code>sudo docker run --rm --user <span class="s2">"</span><span class="k">$(</span>id -u<span class="k">)</span><span class="s2">"</span>:<span class="s2">"</span><span class="k">$(</span>id -g<span class="k">)</span><span class="s2">"</span> -v <span class="s2">"</span><span class="nv">$PWD</span><span class="s2">"</span>:/usr/src/myapp -w /usr/src/myapp rust:latest cargo build --release
</code></pre></div>
<p>This is a great solution for a developer building projects locally.</p>
<h2>3. Continous Integration (CI/CD)</h2>
<p>This one builds on containers.
Now you can select an old enough container and store the resulting artefacts to a webserver.</p>Error when serializing sealed object in meteor2020-09-01T00:00:00+02:002020-09-01T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-09-01:/2020/9/1/error-when-serializing-sealed-object-in-meteor/<p>Today, I worked on a <a href="https://www.meteor.com/">meteor</a> project.
It is a framework building on <a href="https://nodejs.org/">nodejs</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">WebSocket …</a></p><p>Today, I worked on a <a href="https://www.meteor.com/">meteor</a> project.
It is a framework building on <a href="https://nodejs.org/">nodejs</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">WebSocket</a> that takes care of syncing state between the server and the client.</p>
<p>Sadly, one of the building blocks of meteor is no longer maintained.
This means the project is stuck in a technical corner with no way out.
The future will tell if the project or a spiritual successor can be built with the current stack around <code>async</code> and <code>await</code>.</p>
<h3>The problem</h3>
<p>I needed to move a static API dictionary into the configuration.</p>
<p>This means it had to be available when using <code>import Categories</code> because the code requires that.
As well as being controlled by the deployment which feeds <code>Meteor.settings</code>.</p>
<h3>The error</h3>
<p>This lead to this error visible to clients when opening the application:</p>
<div class="highlight"><pre><span></span><code>Couldn't find a template named "findWrap" or "findWrap". Are you sure you defined it?
</code></pre></div>
<p>The reason is that the framework tries to transport a part the object <code>Meteor.settings</code>.
However, that object is <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal">sealed</a>.
So the framework can not inject the needed prototype functions for the serialisation.</p>
<h3>The fix</h3>
<p>We have to copy the content of the sealed object into a live object that the framework can extend and serialize.</p>
<div class="highlight"><pre><span></span><code><span class="c1">// Make a deep copy</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">Categories</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span><span class="w"> </span><span class="nx">Meteor</span><span class="p">.</span><span class="nx">settings</span><span class="p">.</span><span class="kr">public</span><span class="p">.</span><span class="nx">categories</span><span class="p">);</span><span class="w"></span>
<span class="k">export</span><span class="w"> </span><span class="k">default</span><span class="w"> </span><span class="nx">Categories</span><span class="p">;</span><span class="w"></span>
</code></pre></div>color_blinder v0.3.2 maintenance update released2020-08-25T00:00:00+02:002020-08-25T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-08-25:/2020/8/25/color_blinder-v032-maintenance-update-released/<p><a href="https://crates.io/crates/color_blinder/"><img alt="example image" src="https://estada.ch/2020/8/25/color_blinder-v032-maintenance-update-released/2020-08-25_color-blinder-maintenance-update/logo_combined.png"></a></p>
<p>Building a more inclusive experience in software often means, using less subtle interfaces.</p>
<p>Today, I released …</p><p><a href="https://crates.io/crates/color_blinder/"><img alt="example image" src="https://estada.ch/2020/8/25/color_blinder-v032-maintenance-update-released/2020-08-25_color-blinder-maintenance-update/logo_combined.png"></a></p>
<p>Building a more inclusive experience in software often means, using less subtle interfaces.</p>
<p>Today, I released version 0.3.2, a maintenance update.
This release features updated crates leading to a more stable program and adds new image formats like pbm as well as performance improvements for jpeg, bmp and vp8.</p>
<p>I had also noticed that the default encoding is not set to the maximum compression for output files.
Changing the setting did improve the file size of the combined Rust-Lang example from 7.6MB to 6.8MB.
However, this is still far away from the result <a href="https://www.gimp.org/">gimp</a> made with 6.1MB.</p>
<h2>Examples (click to enlarge)</h2>
<h3>RustFest.eu</h3>
<p><a href="./2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_RustFest-2020_combined.png"><img alt="example RustFest 2020" src="https://estada.ch/2020/8/25/color_blinder-v032-maintenance-update-released/2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_RustFest-2020_combined.png"></a></p>
<h3>Rust-Lang.org</h3>
<p><a href="./2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_Rust-Programming-Language_combined.png"><img alt="example Rust Lang 2020" src="https://estada.ch/2020/8/25/color_blinder-v032-maintenance-update-released/2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_Rust-Programming-Language_combined.png"></a></p>
<h3>Google.com</h3>
<p><a href="./2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_Google_combined.png"><img alt="example Google" src="https://estada.ch/2020/8/25/color_blinder-v032-maintenance-update-released/2020-08-25_color-blinder-maintenance-update/Screenshot_2020-08-25_Google_combined.png"></a></p>Video recording technology at RustFest2020-07-30T00:00:00+02:002020-07-30T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-07-30:/2020/7/30/video-recording-technology-at-rustfest/<div class="embed ratio-16-9"><iframe src="https://www.youtube.com/embed/videoseries?list=PL85XCvVPmGQg-dewHRpM08JkGrBPdIVHw" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>
<p>Enjoy the recordings of RustFest Barcelona.</p>
<h1>Managing the stage at RustFest Barcelona</h1>
<p>In 2019 I was …</p><div class="embed ratio-16-9"><iframe src="https://www.youtube.com/embed/videoseries?list=PL85XCvVPmGQg-dewHRpM08JkGrBPdIVHw" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>
<p>Enjoy the recordings of RustFest Barcelona.</p>
<h1>Managing the stage at RustFest Barcelona</h1>
<p>In 2019 I was responsible for the stage during the talks.
Ranging from the prechecks with the venue before the conference (thank you <a href="https://github.com/ferranrego">Ferran</a> and <a href="https://github.com/badboy">Jan-Erik</a>), over to the setup on the talk days and syncing with the MCs during the talks as well as the TimeTable Service displaying the next events between the talks.</p>
<h1>TimeTable Service</h1>
<p><img alt="TimeTable Service" src="https://estada.ch/2020/7/30/video-recording-technology-at-rustfest/2020-07-30_rustfest-video-technology/video-releases-timetable-service.png"></p>
<p>The system is built with an <a href="https://actix.rs/">actix</a>, HTML and JavaScript.
The server reads the schedule directly from the source code of our <a href="https://barcelona.rustfest.eu/">barcelona.rustfest.eu</a> website.
All of this running on a VPS in a datacenter.</p>
<p>Then to feed the image into the various displays of the conference I tried to run the web service from a new Lenovo T495 with windows 10.
Sadly, the output was a grey screen with some flickering.
There appears to have been an incompatibility between the Vega10 graphics card, the (windows) driver and the auditoriums video system.</p>
<p>Luckily, the venue had a backup laptop with an integrated intel graphics card that was compatible.</p>
<h1>The microphones</h1>
<p>We had two stick and one headset microphones.
They are of good quality but not perfect, so there is always some background noise recorded.</p>
<p>Pro tip: make sure all the batteries are charged and that you have spares.
Let us get a quick overview.</p>
<h3>Headset microphones</h3>
<p>The biggest drawback is they have a very prominent side effect with beards: rustling!</p>
<p>One can mitigate that a bit by properly bending the microphone around the speakers cheek.
However, there is a limit to that where the distance becomes to great for the microphone to properly pickup the voice.</p>
<p>Setting up the microphone, its cable and the box containing the battery and the sender must not be rushed.
Very experienced speakers may prefer it when stage crew installs it on them.
For first time speakers, I recommend that they install it themselves.
Especially when the cable is routed under cloths.
In any case: <strong>ask for consent before touching speakers!</strong></p>
<p>Most boxes have a clip on so speakers can clip it onto belts, trousers, skirts or even bras.
Alternatively one can put it into a pocket or similar too.</p>
<p>There are also huge advantages for the recording:</p>
<ul>
<li>The speaker has always the same distance to the microphone giving consistent volume.</li>
<li>There is nothing to hold in ones hands.</li>
<li>Provided the cable is routed properly one can gesture or dance on stage without worrying about the microphone.</li>
</ul>
<h3>Stick microphones</h3>
<p>With these microphones the setup is much quicker:
Just crab it and hold it in front of your mouth.
No touching of the speakers required.</p>
<p>But they have two major challenges:</p>
<ol>
<li>People making tap noises when switching hands or gesturing, especially if they wear a ring.</li>
<li>Speakers have to hold the microphone at a good consistent distance.</li>
</ol>
<p>The second one an on premise technician can soften by making adjustments in volume.</p>
<p>However, because of the weight of the device people tend to lower it unconsciously over the course of their talk.
<strong>Tell them in advance that you may signal them to raise the microphone again during the talk</strong> and it will be a much more pleasant experience for everybody.</p>
<h3>Realtime improvements: audio cut offs</h3>
<p>To combat the rustling, clicking as well as the noise the sound system has modern installations have one or more noise suppression function.</p>
<p>Looking at the resulting sound recording with <a href="https://www.audacityteam.org/">audacity</a> revealing the effects of the system:</p>
<p><img alt="Audio cut offs" src="https://estada.ch/2020/7/30/video-recording-technology-at-rustfest/2020-07-30_rustfest-video-technology/video-releases-audio-cut-offs.png"></p>
<p>The noise was gone, this was perfect for the people in the auditorium, because they would hear the room tone.
But the total silence between every little pause and sentence made it really hard to listen to the talks as a recording or stream.</p>
<p>Passing the audio to an audio improvement service did not yield any improvement.</p>
<h2>Fixing the sound</h2>
<p>After some thinking I had an idea: What if we added natural noise?</p>
<p>The first noise sample was recorded in a room filled with people holding their breath for five seconds at one of our meetups.
The idea was that repeating these five seconds would lead to a nice atmosphere and prevent the harsh cut offs.</p>
<p>After listening to the first rendering it was great.
Easy to listen to and understandable.</p>
<p>So I decided to listen to a talk in a <a href="https://www.zvv.ch/zvv/en/ueber-uns/projekte/tramverbindung-hardbruecke.html">tram</a> for a couple of minutes.
Then it hit me: The noise loop contained one step and one click.
Both would repeat themselves every 5 seconds.
I did not realize it when listening to short slices of 10-15 seconds.
However, after 5 minutes it was disturbing.</p>
<p>The second attempt was to carve out every distraction I could hear with noise cancelling headphones.
This lead to a much shorter noise sample, but it was way cleaner.
Also I tested it by looping the noise itself for a minute and listening to it.</p>
<p>Finally, we added the cleaner noise to the audio tracks and passed the whole thing to an audio improvement service and the result was grand:
Clean sound without any hard cut offs.</p>
<p><img alt="Audio cut offs fixed" src="https://estada.ch/2020/7/30/video-recording-technology-at-rustfest/2020-07-30_rustfest-video-technology/video-releases-audio-cut-offs-fixed.png"></p>
<h2>The stream</h2>
<p>This year we streamed directly from the auditorium to <a href="https://www.youtube.com/watch?list=PL85XCvVPmGQg-dewHRpM08JkGrBPdIVHw&v=o01QmYVluSw&feature=emb_title">youtube</a> using the infrastructure of the university and a google account.
For some reason the software was unable to connect with the stream key only, which we had setup before.</p>
<p>We tested the whole setup on Saturday before the talks.</p>
<p>On Sunday we had a small addition:</p>
<ul>
<li>One of our speakers <a href="https://barcelona.rustfest.eu/sessions/amp-up-web-audio">Niklas Reppel - Amp Up Web Audio Applications with Rust and WebAssembly</a> generated audio on stage to 3.5mm audio jack cable.</li>
</ul>
<p>We tested the audio in the room but forgot to add the channel to the recording and the stream.</p>
<p>Lucky us, our awesome audience recorded the performance and <a href="https://twitter.com/badboy_/">Jan-Erik</a> was able to <a href="https://youtu.be/fCFPl2LrzJc?t=1015">reconstruct most of the performance</a> with samples provided by members of our audience.
Thank you again!</p>Detecting private mode in 20202020-07-11T00:00:00+02:002020-07-11T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-07-11:/2020/7/11/detecting-private-mode-in-2020/<p>This is the update of my <a href="/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/">2018 blog post on why firefox returns an all white …</a></p><p>This is the update of my <a href="/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/">2018 blog post on why firefox returns an all white canvas in private windows</a>.</p>
<p>Recently I noticed that the technique I used on my blog to detect private windows has failed and lead weird artefacts looking a like these:</p>
<p><link rel="stylesheet" type="text/css" href="https://estada.ch/2020/7/11/detecting-private-mode-in-2020/2020-07-11_detecting-private-mode-in-2020/style.css" /></p>
<!-- preload image
![social]({attach}/2020-07-11_detecting-private-mode-in-2020/social.png)
![0]({attach}/2020-07-11_detecting-private-mode-in-2020/0.png)
![1]({attach}/2020-07-11_detecting-private-mode-in-2020/1.png)
![2]({attach}/2020-07-11_detecting-private-mode-in-2020/2.png)
![3]({attach}/2020-07-11_detecting-private-mode-in-2020/3.png)
![4]({attach}/2020-07-11_detecting-private-mode-in-2020/4.png)
![5]({attach}/2020-07-11_detecting-private-mode-in-2020/5.png)
![6]({attach}/2020-07-11_detecting-private-mode-in-2020/6.png)
![7]({attach}/2020-07-11_detecting-private-mode-in-2020/7.png)
-->
<div class="clipped pixelated">
<div id="e0"></div>
<div id="e1"></div>
<div id="e2"></div>
<div id="e3"></div>
<div id="e4"></div>
<div id="e5"></div>
<div id="e6"></div>
<div id="e7"></div>
</div>
<p>Over the last couple releases that simple white surface was replaced with a random sequence of eight colours that repeat all over the canvas.</p>
<p>So, I developed a new detection that is drawing one solid black pixel on a transparent off-screen canvas and then checking if that worked.
This works on both versions of Firefox's algorithm weather it reports an all white canvas or the random stripes.</p>
<p>You can use this code under the <a href="https://opensource.org/licenses/MIT">MIT license</a>, please include a link to this article:</p>
<div class="highlight"><pre><span></span><code><span class="c1">/// LICENSE: MIT by Stefan Schindler - 2020</span><span class="w"></span>
<span class="c1">/// Source: https://estada.ch/2020/7/11/detecting-private-mode-in-2020/</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">16</span><span class="p">,</span><span class="w"> </span><span class="nx">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">16</span><span class="p">;</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">canvas</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'canvas'</span><span class="p">);</span><span class="w"></span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">width</span><span class="p">;</span><span class="w"></span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">height</span><span class="p">;</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">ctx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s1">'2d'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">alpha</span><span class="o">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">});</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">ctx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'canvas not supported'</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="c1">// Set transparency value</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">globalAlpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">1.0</span><span class="p">;</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="nx">width</span><span class="p">,</span><span class="w"> </span><span class="nx">height</span><span class="p">);</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">fillStyle</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'#000'</span><span class="p">;</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">);</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">pixel_0_0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">).</span><span class="nx">data</span><span class="p">;</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">pixel_0_0</span><span class="p">[</span><span class="mf">0</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="w"> </span><span class="o">&&</span><span class="w"></span>
<span class="w"> </span><span class="nx">pixel_0_0</span><span class="p">[</span><span class="mf">1</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="w"> </span><span class="o">&&</span><span class="w"></span>
<span class="w"> </span><span class="nx">pixel_0_0</span><span class="p">[</span><span class="mf">2</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">0</span><span class="w"> </span><span class="o">&&</span><span class="w"></span>
<span class="w"> </span><span class="nx">pixel_0_0</span><span class="p">[</span><span class="mf">3</span><span class="p">]</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">255</span><span class="w"></span>
<span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">// We are in a **normal** window</span><span class="w"></span>
<span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">// We are in a **private** window</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h2>OffscreenCanvas the experimental</h2>
<p>There is a new feature in the works that may change the way one has to work with an off-screen canvas.
Follow the <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1390089">bug 1390089</a> and test <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Experimental_features">this experimental feature</a> by setting <code>gfx.offscreencanvas.enabled</code> to <code>true</code> in <code>about:config</code>.</p>Fixing Salt Fiber Box Wi-Fi AP Separation2020-05-27T00:00:00+02:002020-06-11T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-05-27:/2020/5/27/fixing-salt-fiber-box-ap-separation/<p>Switching ISP is never easy, be it at home or for your business.
This time, the …</p><p>Switching ISP is never easy, be it at home or for your business.
This time, the WLAN was the biggest obstacle.</p>
<h1>What works today</h1>
<p>Salt has a very compelling offer:</p>
<ul>
<li>10gbe uplink</li>
<li>DECT phones (untested)</li>
<li>2.4GHz and 5GHz WLan</li>
<li>Digital TV via Apple TV 4K app</li>
<li>IPv4 <a href="https://en.wikipedia.org/wiki/Network_address_translation">NAT</a></li>
<li>IPv6 public /64 subnet</li>
<li>Backup and restore of the Box-Configuration</li>
</ul>
<p>After the usual two to three week wait for the cable companies technicians to plug the fiber outside of the house in the correct spot for the uplink to work.</p>
<p>Do they actually offer the full 10Gbps?
It is not clear for now because I have no hardware exceeding speeds of 1Gbps</p>
<h1>No NAS, no printers, no (apple|chrome) cast, no gaming, no ping?</h1>
<p>After the initial rush of joy with the working connection I noticed the absence of the printer first.</p>
<p>The symptoms are weird:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Address_Resolution_Protocol">ARP</a> works</li>
<li><a href="https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol">Ping</a> blocked</li>
<li><a href="https://en.wikipedia.org/wiki/Network-attached_storage">NAS</a> not connecting</li>
<li>Missing broadcast information of <a href="https://en.wikipedia.org/wiki/Multicast_DNS">mDNS / bonjour</a></li>
</ul>
<h2>How to disable AP Separation step by step</h2>
<p>First let us get into the WLAN settings on the default IP.
You can find your device on Linux with <code>ip r | grep default</code> over <code>HTTP</code> without encryption.</p>
<p><strong>Warning:</strong> Doing this without a cable connection might trigger some WiFi connection problems.
So using a cable speeds the process up by a lot.</p>
<ol>
<li>Login to the admin panel <ul>
<li>default user: admin</li>
<li>default password: admin</li>
<li><strong>Hint:</strong> This might be a great moment to change the admin password</li>
</ul>
</li>
<li>Switch to <code>expert</code> on the top left</li>
<li>Go to <code>WLAN</code></li>
</ol>
<p>Now we have to do this twice for <em>2.4GHz</em> and <em>5GHz</em>:</p>
<div class="twocolumn">
<ol>
<li>Combined view<ol>
<li>Under $(GHz) -> SSID Enable: <strong>Off</strong></li>
<li>Save settings</li>
</ol>
</li>
<li>WLAN $(GHz)<ol>
<li>Set AP Isolated: <strong>Yes</strong></li>
<li>Save settings</li>
</ol>
</li>
<li>Combined view<ol>
<li>Under $(GHz) -> SSID Enable: <strong>On</strong></li>
<li>Save settings</li>
<li>Under $(GHz) -> SSID Enable: <strong>Off</strong></li>
<li>Save settings</li>
</ol>
</li>
<li>WLAN $(GHz)<ol>
<li>Set AP Isolated: <strong>No</strong></li>
<li>Save settings</li>
</ol>
</li>
<li>Combined view<ol>
<li>Under $(GHz) -> SSID Enable: <strong>On</strong></li>
<li>Save settings</li>
</ol>
</li>
</ol>
</div>
<p>Now repeat for the other frequency.</p>
<p><strong>Update:</strong> Finally, <code>Administration</code> -> <code>Reboot</code> and then reboot the device.</p>
<p>After the reboot internal pings should be possible.
However, it took my setup a minute or two until clients could establish p2p connections.</p>
<h1>Other minor complaints</h1>
<h2>Box itself</h2>
<p>The configuration is encrypted.
People on the internet report that they used to be able to download the configuration as a text file.</p>
<p>Dear Salt, please revert that change.
I am sure your engineers would not mind having plain-text to parse instead of this proprietary format.</p>
<h2>TV</h2>
<p>After testing the apps of <a href="https://www.zdf.de/">ZDF</a>, Netflix, Youtube and <a href="https://tv.salt.ch">SaltTv</a> the verdict in quality is pretty clear:</p>
<p>ZDF and Netflix deliver a clear full HD picture.</p>
<p>Youtube sometimes has a slow start, but after a couple of seconds the image clears to full HD (provided the requested video is available in full HD or better).</p>
<p>The SaltTv app however, is not great.
All the channels have visible artifacts.
This is especially bad when watching a 4K channel one would not expect to see artifacts on a downscaled image.</p>Fixing firefox Lockwise sync2020-05-18T00:00:00+02:002020-05-18T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-05-18:/2020/5/18/fixing-firefox-lockwise-sync/<p>Yesterday I realized that <a href="https://www.mozilla.org/firefox/lockwise/">the password sync Lockwise</a> that is integrated with Firefox Sync stopped working …</p><p>Yesterday I realized that <a href="https://www.mozilla.org/firefox/lockwise/">the password sync Lockwise</a> that is integrated with Firefox Sync stopped working and lost almost all passwords.</p>
<p>Lockwise enables to keep bookmarks and passwords in sync over multiple machines with different operating systems like Arch, Ubuntu, Mac OS X or Windows.</p>
<p>This tutorial requires that you have Firefox Sync setup on at least two machines/profiles.</p>
<h1>Fixing it with X steps</h1>
<ol>
<li>Use another machine/setup and see if the passwords are still there by opening "<img class="inline" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGhlaWdodD0iMzJweCIgaWQ9IkxheWVyXzEiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMzIgMzIiIHdpZHRoPSIzMnB4IiB4bWw6c3BhY2U9InByZXNlcnZlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48cGF0aCBkPSJNNCwxMGgyNGMxLjEwNCwwLDItMC44OTYsMi0ycy0wLjg5Ni0yLTItMkg0QzIuODk2LDYsMiw2Ljg5NiwyLDhTMi44OTYsMTAsNCwxMHogTTI4LDE0SDRjLTEuMTA0LDAtMiwwLjg5Ni0yLDIgIHMwLjg5NiwyLDIsMmgyNGMxLjEwNCwwLDItMC44OTYsMi0yUzI5LjEwNCwxNCwyOCwxNHogTTI4LDIySDRjLTEuMTA0LDAtMiwwLjg5Ni0yLDJzMC44OTYsMiwyLDJoMjRjMS4xMDQsMCwyLTAuODk2LDItMiAgUzI5LjEwNCwyMiwyOCwyMnoiLz48L3N2Zz4="> Hamburger menu 🍔" -> "Accounts and Passwords". If they are gone, this will be a <a href="https://www.youtube.com/watch?v=4N3N1MlvVc4">sad day in a mad world</a>.</li>
<li>Click on "your own picture" -> "your email address" -> "Manage account".</li>
<li>In the new tab under "Devices & apps" click "Show".</li>
<li>Now, if there is one or more entries with the same name this is (probably) causes the sync of Lockwise to fail.</li>
<li>To rename a device click "your own picture" -> "Sync Settings".</li>
<li>In the now open settings tab "Device Name" -> "Change Device Name..." now choose a unique name for all the machines/profiles that stopped working.</li>
<li>On the same page press "Sync Now" on the working machine/profile first, then after a quick wait (eg. 30 seconds) press "Sync Now" on the broken machines/profiles.</li>
</ol>
<h2>It did not work ...</h2>
<p>You can try this on the broken profiles:</p>
<ol>
<li>In the settings from the last step.</li>
<li>Disable the password sync then "Sync Now"</li>
<li>Enable the password sync then "Sync Now"</li>
</ol>
<p>This is another trigger to get Lockwise to sync again.</p>cargo crev and cargo audit 🦀 the second online meetup ☁2020-04-30T00:00:00+02:002020-04-30T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-04-30:/2020/4/30/cargo-crev-and-cargo-audit-the-second-online-meetup/<p><a href="https://www.youtube.com/watch?v=_xS40wqO8GA"><img alt="watch the recording" src="https://estada.ch/images/2020-04-30_cargo-crev-and-cargo-audit-online-meetup.png"></a></p>
<p>Christian (aka chrysn) talked about cargo-crev: «Don't waste time reviewing crates – make it count instead! With …</p><p><a href="https://www.youtube.com/watch?v=_xS40wqO8GA"><img alt="watch the recording" src="https://estada.ch/images/2020-04-30_cargo-crev-and-cargo-audit-online-meetup.png"></a></p>
<p>Christian (aka chrysn) talked about cargo-crev: «Don't waste time reviewing crates – make it count instead! With crev code reviews, we can mesh up our reviews to gain a level of review coverage on, and thus trust in, our dependencies that would be practically unobtainable alone. Let's join forces to cover our whole dependency trees.»</p>
<ul>
<li><a href="https://christian.amsuess.com/presentations/2020/crev/">Slides: christian.amsuess.com/presentations/2020/crev/</a></li>
<li><a href="https://crates.io/crates/cargo-crev">crates.io</a></li>
</ul>
<p>Tony Arcieri (aka tarcieri / bascule) talked about cargo-audit and the RustSec Advisory Database.</p>
<ul>
<li><a href="https://crates.io/crates/cargo-audit">crates.io</a></li>
</ul>
<p>Stay tuned for our next <a href="https://www.meetup.com/Rust-Zurich/">Rust Zürisee meetup</a> in the near future.</p>
<p>Enjoy the recording 📹</p>Installing Rust on Windows 10 (native)2020-04-19T00:00:00+02:002020-04-19T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-04-19:/2020/4/19/installing-rust-on-windows-10-native/<p>This is a quick how to for Windows 10 2004.</p>
<ol>
<li>Download and run <a href="https://rustup.rs">rustup.rs</a></li>
<li>Download …</li></ol><p>This is a quick how to for Windows 10 2004.</p>
<ol>
<li>Download and run <a href="https://rustup.rs">rustup.rs</a></li>
<li>Download <a href="https://visualstudio.microsoft.com/downloads/">Build Tools for Visual Studio 2019</a> hidden under "Tools for Visual Studio 2019"</li>
<li>Run "Build Tools for Visual Studio 2019 Installer" and select<ul>
<li>C++ Tools</li>
</ul>
</li>
<li>While in the C++ Tools you have to select "Windows 10 SDK" as well. There will be multiple options, go with the highest version number.</li>
</ol>
<h1>Testing the installation</h1>
<ol>
<li>Open a <strong>new</strong> PowerShell or "Command Prompt" and type the following commands. Each one must return without error.</li>
<li>Go to the temporary directory: <code>cd %TEMP%</code></li>
<li>Create a new test project: <code>cargo new toolchain_test</code></li>
<li>Enter the newly created directory: <code>cd toolchain_test</code></li>
<li>Build and run the "Hello, world!" program: <code>cargo run</code></li>
</ol>
<p>Now you should see some compiling and then <code>Hello, world!</code></p>
<h1>Possible errors</h1>
<h2>The tools/compiler just stopped working</h2>
<p>Under Windows you have to keep track of which environment you are using.</p>
<ul>
<li><code>Command Prompt</code> / <code>cmd</code> and <code>PowerShell</code> are the <strong>native Windows</strong> environment. It is the same as when you start a program from the start menu. </li>
<li><a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10">Windows Subsystem for Linux (<code>WSL</code>)</a> or <a href="https://docs.microsoft.com/en-us/windows/wsl/wsl2-install">WSL 2</a> are mostly isolated POSIX environments running on top of Windows. Applications installed inside a WSL might not be visible from outside.</li>
<li><code>MINGW64</code> is similar to WSL 1. It is an overlay installation designed to run Linux programs on Windows. It is sometimes installed my other software like <a href="https://git-scm.com/">git</a>.</li>
</ul>
<p>You can use rustup inside the encapsulated environments too.
However, then the programs created depend on their environment.
So you would have to cross-compile them for <code>stable-x86_64-pc-windows-msvc</code> the native Windows environment.</p>
<h2>cl.exe exits with error code 2 or "stdlib.h" not found:</h2>
<p>You are probably missing the "Windows 10 SDK".</p>
<ol>
<li>Restart the "Build Tools for Visual Studio 2019 Installer"</li>
<li>In the tile "Visual Studio Build Tools 2019" press "Modify"</li>
<li>Under C++ Tools enable the "Windows 10 SDK", which at the time of writing is <code>10.0.18362.0</code>.</li>
</ol>
<p><img alt="Add Windows 10 SDK" src="https://estada.ch/2020/4/19/installing-rust-on-windows-10-native/images/2020-04-19_add-sdk.png"></p>
<h2>cargo command not found</h2>
<p>You are probably missing cargo in your <code>%PATH%</code>.</p>
<ul>
<li>First, Open a new and fresh shell and try there. The <code>%PATH%</code> variable is only loaded when the terminal starts.</li>
<li>If that did not work, you can execute rustup.exe again and re-install the rust toolchain</li>
<li>Or you can edit the PATH variable by hand in the computers settings.</li>
</ul>🐍 PyO3 and Rust 🦀 - an experimental online meetup ☁2020-04-08T00:00:00+02:002020-04-08T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-04-08:/2020/4/8/pyo3-and-rust-an-experimental-online-meetup/<p><a href="https://www.youtube.com/watch?v=Y3e1BwGDOxc"><img alt="watch the recording" src="https://estada.ch/images/2020-04-08_pyo3-rust-zurisee-meetup.png"></a></p>
<p>For our second <a href="https://www.meetup.com/Rust-Zurich/">Rust Zürisee meetup</a> in 2020 we intended to go big.
However, with the …</p><p><a href="https://www.youtube.com/watch?v=Y3e1BwGDOxc"><img alt="watch the recording" src="https://estada.ch/images/2020-04-08_pyo3-rust-zurisee-meetup.png"></a></p>
<p>For our second <a href="https://www.meetup.com/Rust-Zurich/">Rust Zürisee meetup</a> in 2020 we intended to go big.
However, with the corona crisis going on gatherings and meetings became impossible due to the shutdown.</p>
<p>Enjoy the recording 📹</p>Why use ­ when your design breaks?2020-03-30T00:00:00+02:002020-03-30T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-03-30:/2020/3/30/why-use-shy-when-your-design-breaks/<p>Creating a mobile first website has become the standard nowadays.
Tools like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">flexbox</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid">grid-layout</a> allow …</p><p>Creating a mobile first website has become the standard nowadays.
Tools like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox">flexbox</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid">grid-layout</a> allow to build nice looking designs.</p>
<p>However, there is still one problem the machine can not take from you: Long words</p>
<p>Imagine your users start using words like this: SchiffsKapitänMützenFabrikantenTochter</p>
<h1>The problem</h1>
<p>So what happens on mobile devices with a limited screen width?</p>
<div>
SchiffsKapitänMützenFabrikantenTochter
</div>
<h1>Solution: <code>&shy;</code> (U+00AD)</h1>
<p><code>&shy;</code>, short for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/hyphens">soft hyphen</a>, is invisible if there is enough space, but inserts a <code>-</code> if the line has to be broken a that point.</p>
<div>
Schiffs­Kapitän­Mützen­Fabrikanten­Tochter
</div>
<p>We can also use the Unicode character (U+00AD), which is fun because it also works in most editors:</p>
<div>
SchiffsKapitänMützenFabrikantenTochter
</div>
<h2>The alternative <code><wbr></code> or zero width space (U+200B)</h2>
<p><code><wbr></code> has a similar behaviour, but does not insert the dash.
Also, it does not work with internet explorer 8-11 (it works with 6 and 7).</p>
<div>
Schiffs<wbr>Kapitän<wbr>Mützen<wbr>Fabrikanten<wbr>Tochter
</div>
<p>We can also construct the same result with <a href="https://en.wikipedia.org/wiki/Zero-width_space">ZWSP</a>:</p>
<div>
SchiffsKapitänMützenFabrikantenTochter
</div>
<h1>Future work</h1>
<p>To me it would be very interesting to see a more programs inserting soft hyphens into user generated content to prevent these kinds of design breaks.
Instead of designers simply adding <code>overflow: hidden;</code> and essentially hiding peoples texts.</p>
<p><link rel="stylesheet" type="text/css" href="https://estada.ch/2020/3/30/why-use-shy-when-your-design-breaks/2020-03-30_shy-wbr.css" /></p>Using skype web with Firefox2020-02-28T00:00:00+01:002020-02-28T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-02-28:/2020/2/28/using-skype-web-with-firefox/<p>Skype has a WebUI that does not allow one to use Firefox for some reason.
Lucky …</p><p>Skype has a WebUI that does not allow one to use Firefox for some reason.
Lucky for us, the check is done with the User Agent.
So we can spoof that.</p>
<h1>Spoof User Agent</h1>
<ol>
<li>First <a href="https://addons.mozilla.org/firefox/addon/user-agent-string-switcher/?src=estada.ch">install the <strong>User-Agent Switcher and Manager Addon</strong></a></li>
<li><strong>Optional:</strong> Enable the addon in <strong>private Windows</strong></li>
<li><strong>Preferences:</strong> Switch to <strong>White-List Mode</strong> and set these domains
<code>skype.com, live.com, microsoft.com</code>
and press <strong>Save</strong></li>
<li><strong>Toolbar Icon:</strong> Select the latest Chrome from the list</li>
<li>Navigate to <a href="https://web.skype.com">web.skype.com</a> (in a private Window if you prefer)</li>
</ol>Switching from CSS to HTML generated "," and Telegram instantview implementation2020-01-20T00:00:00+01:002020-01-20T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-01-20:/2020/1/20/switching-from-css-to-html-generated-and-telegram-instantview-implementation/<p>Implementing the instantview feature has been on my list for a while now.
Today I implemented …</p><p>Implementing the instantview feature has been on my list for a while now.
Today I implemented it for this website.</p>
<p>Having a static website built by a generator makes this relatively easy.</p>
<h2>Changes to this website</h2>
<p>With the new year 2020 I started tagging my content.
The default implementation did not yield commas between the tags.</p>
<p>So in the first instance I implemented them with CSS:</p>
<div class="highlight"><pre><span></span><code><span class="nt">footer</span><span class="p">.</span><span class="nc">post-info</span><span class="w"> </span><span class="p">.</span><span class="nc">tags</span><span class="w"> </span><span class="nt">a</span><span class="p">::</span><span class="nd">after</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s2">", "</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="nt">footer</span><span class="p">.</span><span class="nc">post-info</span><span class="w"> </span><span class="p">.</span><span class="nc">tags</span><span class="w"> </span><span class="nt">a</span><span class="p">:</span><span class="nd">last-child</span><span class="p">::</span><span class="nd">after</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>That meant I had to fix them in the instantview too:</p>
<div class="highlight"><pre><span></span><code>@map<span class="o">(</span><span class="nv">$body</span>/div<span class="o">[</span>@class<span class="o">=</span><span class="s2">"tags"</span><span class="o">]</span>/a<span class="o">)</span> <span class="o">{</span>
<span class="nv">$a</span>: <span class="nv">$@</span>
@if_not<span class="o">(</span> <span class="nv">$last</span> <span class="o">)</span> <span class="o">{</span>
@append_to<span class="o">(</span><span class="nv">$a</span><span class="o">)</span>: <span class="s2">", "</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>Now the "hot fixing" has gone too far.</p>
<h2>Fixing the jinja2 template</h2>
<div class="highlight"><pre><span></span><code><span class="cp">{%</span> <span class="k">if</span> <span class="nv">article.tags</span> <span class="cp">%}</span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tags"</span><span class="nt">></span>
Tags:
<span class="cp">{%</span> <span class="k">for</span> <span class="nv">tag</span> <span class="k">in</span> <span class="nv">article.tags</span> <span class="o">|</span> <span class="nf">sort</span> <span class="cp">%}</span>
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"</span><span class="cp">{{</span> <span class="nv">SITEURL</span> <span class="cp">}}</span><span class="s">/</span><span class="cp">{{</span> <span class="nv">tag.url</span> <span class="cp">}}</span><span class="s">"</span><span class="nt">></span><span class="cp">{{</span> <span class="nv">tag</span> <span class="cp">}}</span><span class="nt"></a></span>
<span class="cp">{%</span>- <span class="k">if</span> <span class="k">not</span> <span class="nb">loop</span><span class="nv">.last</span> -<span class="cp">%}</span>, <span class="cp">{%</span>- <span class="k">endif</span> -<span class="cp">%}</span>
<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
<span class="nt"></div></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span>
</code></pre></div>
<p>The important difference are the <code>-</code> inside the if statements.
The dashes turn result in an inline block instead of and indented + newline separated block.</p>
<h1>Instantview</h1>
<p>The instantview rules are now on the telegram service.
So the feature could get activated for my domain now any time.</p>
<p>However, since the contest in 2019 was over the communication pretty much stopped.
It is not clear what their process is or if the even add new domains at this point.</p>
<h2>The rules</h2>
<div class="highlight"><pre><span></span><code>~version: <span class="s2">"2.1"</span>
<span class="c1">### STEP 1: Define which pages get Instant View and which don't</span>
<span class="c1"># Articles</span>
?exists: /html/head/meta<span class="o">[</span>@property<span class="o">=</span><span class="s2">"article:published_time"</span><span class="o">]</span>
<span class="c1">### STEP 2: Define the essential elements</span>
<span class="c1"># The 'body' and 'title' *properties* are required for an Instant View page to work. </span>
<span class="c1"># 'Subtitle' is optional for IV pages, but Medium posts can have subtitles, so it is essential that we reflect this in our template.</span>
body: //div<span class="o">[</span>@class<span class="o">=</span><span class="s2">"entry-content"</span><span class="o">]</span>
<span class="c1">#@debug</span>
title: //h2.entry-title/a
<span class="c1">#author: //address[@class="vcard author"]/a</span>
<span class="c1">### STEP 3: Further refinements</span>
<span class="c1"># extract tags for newer articles</span>
@prepend_to<span class="o">(</span><span class="nv">$body</span><span class="o">)</span>: //footer<span class="o">[</span>@class<span class="o">=</span><span class="s2">"post-info"</span><span class="o">]</span>/div<span class="o">[</span>@class<span class="o">=</span><span class="s2">"tags"</span><span class="o">]</span>
<span class="c1"># remove all svg images, sadly</span>
@replace_tag<span class="o">(</span><pre><span class="o">)</span>: //img<span class="o">[</span>contains<span class="o">(</span>@src, <span class="s2">".svg"</span><span class="o">)]</span>
<span class="c1"># extract images from <p></span>
@split_parent: //p/img
<span class="c1">## STEP 4: Cleanup</span>
@remove: //section.extras
@remove: //footer
@remove: //canvas
@remove: //script
<span class="c1">### Index pages</span>
?not_exists: /html/head/meta<span class="o">[</span>@property<span class="o">=</span><span class="s2">"article:published_time"</span><span class="o">]</span>
body: //section<span class="o">[</span>@id<span class="o">=</span><span class="s2">"content"</span><span class="o">]</span>
<span class="c1">#@debug</span>
title: //h2.entry-title/a
<span class="c1"># remove all svg images, sadly</span>
@replace_tag<span class="o">(</span><pre><span class="o">)</span>: //img<span class="o">[</span>contains<span class="o">(</span>@src, <span class="s2">".svg"</span><span class="o">)]</span>
@replace_tag<span class="o">(</span><pre><span class="o">)</span>: //section<span class="o">[</span>@id<span class="o">=</span><span class="s2">"content"</span><span class="o">]</span>/footer
<span class="c1">#@debug</span>
@replace_tag<span class="o">(</span><div><span class="o">)</span>: //footer<span class="o">[</span>@class<span class="o">=</span><span class="s2">"post-info"</span><span class="o">]</span>
<span class="c1">#@debug</span>
<span class="c1"># make post visible</span>
@replace_tag<span class="o">(</span><section><span class="o">)</span>: <span class="nv">$body</span>//ol<span class="o">[</span>@id<span class="o">=</span><span class="s2">"post-list"</span><span class="o">]</span>/li
@replace_tag<span class="o">(</span><div><span class="o">)</span>: //ol<span class="o">[</span>@id<span class="o">=</span><span class="s2">"post-list"</span><span class="o">]</span>
<span class="c1"># extract images from <a> and <p> (order matters)</span>
@split_parent: //a/img
@split_parent: //p/img
@remove: //section<span class="o">[</span>@id<span class="o">=</span><span class="s2">"content"</span><span class="o">]</span>/h2
@remove: //section.extras
@remove: //hr
<span class="c1">#@remove: //footer</span>
@remove: //canvas
@remove: //script
</code></pre></div>Debugging multipath problems2020-01-08T00:00:00+01:002020-01-08T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-01-08:/2020/1/8/debugging-multipath-problems/<p>When your kubernetes cluster hangs, you are having a bad day.
One symptom you might notice …</p><p>When your kubernetes cluster hangs, you are having a bad day.
One symptom you might notice is you can no longer create or delete PVC's or Pods.</p>
<p>Checking if the command <code>sudo multipath -ll</code> hangs will tell you if you are having a problem with your SAN.</p>
<h1>Workaround</h1>
<p>Open a second shell and run this:</p>
<div class="highlight"><pre><span></span><code>sudo multipath -ll -v4
</code></pre></div>
<p>The command will hang on a line like this containing the device name next to the timestamp:</p>
<div class="highlight"><pre><span></span><code>...
Jan <span class="m">08</span> HH:MM:SS <span class="p">|</span> sdaw: get_state
Jan <span class="m">08</span> HH:MM:SS <span class="p">|</span> sdaw: <span class="nv">detect_checker</span> <span class="o">=</span> yes <span class="o">(</span>setting: multipath internal<span class="o">)</span>
</code></pre></div>
<p>Now in the first shell become root with <code>sudo -i</code> and prepare this little helper function:</p>
<div class="highlight"><pre><span></span><code>del_dev<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="m">1</span> > /sys/block/<span class="si">${</span><span class="nv">1</span><span class="si">}</span>/device/delete<span class="p">;</span> <span class="o">}</span><span class="p">;</span>
</code></pre></div>
<p>Now you can delete the hanging device like this:</p>
<div class="highlight"><pre><span></span><code>del_dev sdaw
</code></pre></div>
<p>Then <code>multipath</code> will continue.</p>
<p>Finally repeat the process for every hanging LUN until <code>multipath</code> returns.</p>pfsense on kvm broken NAT2020-01-04T00:00:00+01:002020-01-04T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2020-01-04:/2020/1/4/pfsense-on-kvm-broken-nat/<p>I am currently setting up a <a href="https://k3s.io/">k3s</a> cloud test environement on a single physical machine with …</p><p>I am currently setting up a <a href="https://k3s.io/">k3s</a> cloud test environement on a single physical machine with <a href="https://www.linux-kvm.org/">KVM</a> and <a href="https://libvirt.org/">libvirt</a>.
To isolate the setup I choose a bridge for all the VMs and a pfsense to bridge it into the real world.</p>
<p>While testing I noticed that no updated were installed.
Pinging worked and TCP from the pfsense itself worked too.</p>
<p>So was the NAT broken? According to this <a href="https://forum.netgate.com/topic/79004/pfsense-2-2-not-passing-traffic-but-ping-does-get-through">forum thread starting in 2015</a> the reason was the <code>virtio</code> network card.</p>
<h1>The workaround</h1>
<p>Using the <code>e1000</code> driver works and is fast enough.</p>Rust Assembly at CCCongress 36c3 🦀2019-12-27T00:00:00+01:002019-12-27T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-12-27:/2019/12/27/rust-assembly-at-cccongress-36c3/<p><img alt="The assembly table" src="https://estada.ch/2019/12/27/rust-assembly-at-cccongress-36c3/images/2019-12-27_36c3-rust-assembly.jpg"></p>
<p>This year I can not attend the conference myself.
However, I managed to get a table …</p><p><img alt="The assembly table" src="https://estada.ch/2019/12/27/rust-assembly-at-cccongress-36c3/images/2019-12-27_36c3-rust-assembly.jpg"></p>
<p>This year I can not attend the conference myself.
However, I managed to get a table again. 🎉</p>
<p>This years table has 20 seats and a wooden logo made by <a href="https://twitter.com/Ammoniak">Raphael Theiler</a>. Thank you!</p>
<p><a href="https://users.rust-lang.org/t/36c3-assembly-table-ccc-leipzig/34040">Follow this forum post</a> to stay in touch with people <a href="https://36c3.c3nav.de/l/rust/">on site</a>.</p>RustFest Barcelona2019-11-09T00:00:00+01:002019-12-01T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-11-09:/2019/11/9/rustfest-barcelona/<p><img alt="Family photo" src="https://estada.ch/2019/11/9/rustfest-barcelona/images/2019-11-09_organizing-rustfest-family-pic.jpg"></p>
<p>This weekend I am co-organizing the four day community conference around the <a href="https://www.rust-lang.org/">rust language</a> starting today …</p><p><img alt="Family photo" src="https://estada.ch/2019/11/9/rustfest-barcelona/images/2019-11-09_organizing-rustfest-family-pic.jpg"></p>
<p>This weekend I am co-organizing the four day community conference around the <a href="https://www.rust-lang.org/">rust language</a> starting today until Tuesday.</p>
<p>Some stats:</p>
<ul>
<li>The 6th <a href="https://barcelona.rustfest.eu/">RustFest</a> I attended</li>
<li>The 3rd I Co-Organized in the core team</li>
<li>Having hosted <a href="https://zurich.rustfest.eu/">RustFest Zürich</a></li>
</ul>
<p>I made a lot of grand memories :)</p>color_blinder lightning talk at rust Zürichsee2019-10-28T00:00:00+01:002019-10-28T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-10-28:/2019/10/28/color_blinder-lightning-talk-at-rust-zurichsee/<p>After Claudia gave her <a href="https://barcelona.rustfest.eu/sessions/66-hours-of-rust">66 hours of Rust</a> talk at our meetup I presented my project …</p><p>After Claudia gave her <a href="https://barcelona.rustfest.eu/sessions/66-hours-of-rust">66 hours of Rust</a> talk at our meetup I presented my project: <a href="https://crates.io/crates/color_blinder">color_blinder</a></p>
<ul>
<li><a href="https://gitlab.com/dns2utf8/color_blinder">The source code</a>.</li>
<li><a href="https://github.com/rust-zurichsee/meetups/tree/master/2019-10-28_66h-of-rust">The slides</a></li>
</ul>
<p>Last but not least, <a href="https://github.com/reyk/retris">reyk presented his rETRIS</a>.
A game he made with his kids.</p>CORS errors with SVG and Internet Explorer2019-10-18T00:00:00+02:002019-10-18T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-10-18:/2019/10/18/cors-errors-with-svg-and-internet-explorer/<p>When working on my latest project <a href="https://nocloud.website">nocloud.website</a> I encountered a strange error in production:</p>
<div class="highlight"><pre><span></span><code>Content …</code></pre></div><p>When working on my latest project <a href="https://nocloud.website">nocloud.website</a> I encountered a strange error in production:</p>
<div class="highlight"><pre><span></span><code>Content Security Policy: The page’s settings blocked the loading of a resource at inline <span class="o">(</span>“default-src”<span class="o">)</span>.
</code></pre></div>
<h1>Internet Explorer 11</h1>
<p>IE does not know about the <code>connect-src</code> directive.
Sadly now IE ignores the complete policy.</p>
<div class="highlight"><pre><span></span><code><span class="nx">fetch</span><span class="p">(</span><span class="s2">"https://httpbin.org/get"</span><span class="p">)</span><span class="w"></span>
<span class="c1">// [object Promise]: {}</span><span class="w"></span>
<span class="c1">// [object Response]: {body: ReadableStream, bodyUsed: false, headers: Headers, ok: true, redirected: false...}</span><span class="w"></span>
</code></pre></div>
<p>So this means that with the raise of new features IE users even loose the protection that they had when their browser was built.</p>
<h2>Expected result eg. on Firefox:</h2>
<div class="highlight"><pre><span></span><code><span class="nx">fetch</span><span class="p">(</span><span class="s2">"https://httpbin.org/get"</span><span class="p">)</span><span class="w"></span>
<span class="c1">// Promise { <state>: "rejected" }</span><span class="w"></span>
<span class="c1">// Content Security Policy: The page’s settings blocked the loading of a resource at https://httpbin.org/get (“default-src”).</span><span class="w"></span>
</code></pre></div>Release update of heise-single-page addon2019-10-10T00:00:00+02:002019-10-10T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-10-10:/2019/10/10/release-update-of-heise-single-page-addon/<p>I just released version 1.2.0 that includes a hint for the incognito mode: <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/incognito">spanning …</a></p><p>I just released version 1.2.0 that includes a hint for the incognito mode: <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/incognito">spanning</a></p>
<ul>
<li><a href="https://addons.mozilla.org/en-US/firefox/addon/heise-single-page/">Addon Mozilla</a></li>
<li><a href="https://gitlab.com/dns2utf8/heise_single_page">Source code</a></li>
</ul>
<p>For Chrome users, you have to <a href="https://gitlab.com/dns2utf8/heise_single_page/raw/master/web-ext-artifacts/heise_single_page-1.2.0.zip?inline=false">download the extension directly</a>.</p>Fixing meteor test CI timeout2019-09-26T00:00:00+02:002019-09-26T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-09-26:/2019/9/26/fixing-meteor-test-ci-timeout/<p>In the beginning of this year I started helping <a href="https://gitlab.com/Openki/Openki">Openki</a> to integrate their test suite with …</p><p>In the beginning of this year I started helping <a href="https://gitlab.com/Openki/Openki">Openki</a> to integrate their test suite with GitLab's CI.</p>
<p>We built on their existing test script.
The original script is used for local testing.
This is the final script running inside the CI infrastructure:</p>
<div class="highlight"><pre><span></span><code><span class="ch">#!/bin/bash</span>
<span class="nb">set</span> -xe
<span class="nb">export</span> <span class="nv">METEOR_ALLOW_SUPERUSER</span><span class="o">=</span><span class="s1">'true'</span>
apt-get update -qq <span class="o">&&</span> apt-get upgrade -qqy
apt-get install -qq build-essential python git libxss1 libappindicator1 libindicator7 curl wget xvfb libxtst6 libxss1 libgconf2-4 libnss3 libgtk2.0-0 libgtk-3-0 libasound2
curl -sL https://deb.nodesource.com/setup_8.x <span class="p">|</span> bash -
<span class="nb">export</span> <span class="nv">DISPLAY</span><span class="o">=</span><span class="s1">':99.0'</span>
Xvfb <span class="s2">"</span><span class="si">${</span><span class="nv">DISPLAY</span><span class="si">}</span><span class="s2">"</span> -screen <span class="m">0</span> 1024x768x24 > /dev/null <span class="m">2</span>><span class="p">&</span><span class="m">1</span> <span class="p">&</span>
<span class="c1"># Download Meteor</span>
<span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:<span class="nv">$HOME</span>/.meteor
mkdir -p .meteor/ .npm/ node_modules/
<span class="k">if</span> <span class="o">[</span> ! -e <span class="nv">$HOME</span>/.meteor/meteor <span class="o">]</span><span class="p">;</span> <span class="k">then</span> curl -k https://install.meteor.com <span class="p">|</span> sh<span class="p">;</span> <span class="k">fi</span>
<span class="c1">#install google-chrome</span>
<span class="c1">#wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -</span>
<span class="c1">#sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'</span>
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
apt-get install -qqy ./google-chrome*.deb
rm google-chrome-stable_current_amd64.deb
mv /usr/bin/google-chrome /usr/bin/google-chrome-original
<span class="nb">printf</span> <span class="s1">'#!/bin/bash\n\ngoogle-chrome-original --no-sandbox --headless --disable-setuid-sandbox --disable-gpu --enable-debugging "$@"\n'</span> > /usr/bin/google-chrome
chmod +x /usr/bin/google-chrome
<span class="nb">export</span> <span class="nv">CHROME_BIN</span><span class="o">=</span>/usr/bin/google-chrome
meteor update --patch
meteor npm i
meteor node -v <span class="o">&&</span> meteor npm version
meteor npm run app-test
</code></pre></div>
<p>We tried many different containers and configurations.
However, the tests would timeout and fail on most systems and the CI.</p>
<p>So we started comparing the systems to find differences in behaviour.
I started with the obvious and found some lower bounds:</p>
<ul>
<li>Available memory, at least 512MB must be free</li>
<li>Num CPUs, at least two CPUs</li>
<li>Storage<ul>
<li>Space, at least 2GB free before the job starts</li>
<li>HDD vs. SSD, both would fail most of the time</li>
</ul>
</li>
<li>Kernel, old or new no pattern </li>
</ul>
<h1>Months later ...</h1>
<p>After months of randomly changing parameters I had an this week:
What if the response time was a problem?</p>
<ul>
<li>What if our old testing hardware was to slow?</li>
<li>What if the CI instance running on GCE had not enough IO Bandwidth?</li>
</ul>
<p>Lucky for us GitLab offers internal runners that one can enable by tagging the jobs with <code>gitlab-org</code>:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt">tags</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gitlab-org</span><span class="w"></span>
</code></pre></div>
<p>In addition the test now runs 3-4 times faster, which is great.</p>
<p>Here is the complete <code>.gitlab-ci.yml</code>:</p>
<div class="highlight"><pre><span></span><code><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ubuntu:latest</span><span class="w"></span>
<span class="nt">stages</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cleanup</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">deploy</span><span class="w"></span>
<span class="nt">cache</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">paths</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">node_modules/</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">.npm/</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">.meteor/</span><span class="w"></span>
<span class="nt">variables</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="c1"># DEBUG: "selenium*, chromedriver*"</span><span class="w"></span>
<span class="w"> </span><span class="nt">DISPLAY</span><span class="p">:</span><span class="w"> </span><span class="s">":99.0"</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Reduce git traffic</span><span class="w"></span>
<span class="w"> </span><span class="nt">GIT_DEPTH</span><span class="p">:</span><span class="w"> </span><span class="s">"10"</span><span class="w"></span>
<span class="w"> </span><span class="nt">METEOR_ALLOW_SUPERUSER</span><span class="p">:</span><span class="w"> </span><span class="s">"true"</span><span class="w"></span>
<span class="w"> </span><span class="nt">TOOL_NODE_FLAGS</span><span class="p">:</span><span class="w"> </span><span class="s">'--max_old_space_size=4096'</span><span class="w"></span>
<span class="nt">cleanup</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">cleanup</span><span class="w"></span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">rm -rf .meteor/ .npm/ node_modules/</span><span class="w"></span>
<span class="w"> </span><span class="nt">only</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">staging</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">master</span><span class="w"></span>
<span class="nt">test</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">test</span><span class="w"></span>
<span class="w"> </span><span class="nt">tags</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gitlab-org</span><span class="w"></span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">./ci/test2.sh</span><span class="w"></span>
</code></pre></div>Recording: "Distributed and Secure Systems with Rust"2019-08-15T00:00:00+02:002019-08-15T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-08-15:/2019/8/15/recording-distributed-and-secure-systems-with-rust/<p><a href="https://media.ccc.de/v/cosin-28-distributed_actor_system_with_rust"><img alt="2019-08-15_preview-distributed-actor-system.jpg" src="https://estada.ch/2019/8/15/recording-distributed-and-secure-systems-with-rust/images/2019-08-15_preview-distributed-actor-system.jpg"></a></p>Climate bar codes2019-08-05T00:00:00+02:002019-08-05T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-08-05:/2019/8/5/climate-bar-codes/<p><img alt="EUROPE-Switzerland--1864-2018-MS.png" src="https://estada.ch/2019/8/5/climate-bar-codes/images/2019-08-05_EUROPE-Switzerland--1864-2018-MS.png"></p>
<p>This image shows the average temperature per year in Switzerland from 1864 to 2018. Blue years …</p><p><img alt="EUROPE-Switzerland--1864-2018-MS.png" src="https://estada.ch/2019/8/5/climate-bar-codes/images/2019-08-05_EUROPE-Switzerland--1864-2018-MS.png"></p>
<p>This image shows the average temperature per year in Switzerland from 1864 to 2018. Blue years are cooler than average 1971-2000, reds are hotter than average.</p>
<p>For comparison, the global image from 1850 to 2018:
<img alt="GLOBE---1850-2018-MO.png" src="https://estada.ch/2019/8/5/climate-bar-codes/images/2019-08-05_GLOBE---1850-2018-MO.png"></p>
<p>All the graphics are from <a href="https://showyourstripes.info/">showyourstripes.info</a> made by climate scientist <a href="http://www.met.reading.ac.uk/~ed/">Ed Hawkins</a>.</p>RAID Controller stuck in BIOS POST2019-07-09T00:00:00+02:002019-07-09T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-07-09:/2019/7/9/raid-controller-stuck-in-bios-post/<p>One of my customers ran into the problem of the main VM host not continuing to …</p><p>One of my customers ran into the problem of the main VM host not continuing to boot further than the <a href="https://en.wikipedia.org/wiki/Power-on_self-test">BIOS POST</a>.</p>
<p>I had a hunch that some of the failed disks had been messing with the controller since we have been replacing several disks in that machine this year and the system had always rebooted so far.</p>
<p>However, this time the defective disk was the first one.
So my guess is that the RAID controller assumed the first disk to be correct if it was present, thereby rendering all the working disks invalid which then lead to a massively degraded state.</p>
<p><strong>Solution:</strong> Pull out the first disk and press <code>Ctrl</code> + <code>Alt</code> + <code>Delete</code>.</p>My talk "Distributed Actor System with Rust" is public2019-06-12T00:00:00+02:002019-06-12T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-06-12:/2019/6/12/my-talk-distributed-actor-system-with-rust-is-public/<p>My talk starts at 2019-06-15 Saturday 17:15 <a href="https://www.cosin.ch/fahrplan/2019/events/28.html">cosin 2019</a> is announced. </p>JavaScript Array.prototype.flatMap Polyfill2019-06-10T00:00:00+02:002020-06-03T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-06-10:/2019/6/10/javascript-arrayprototypeflatmap-polyfill/<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap"><code>flatMap</code></a> method is identical to a <code>map</code> followed by a call to <code>flat</code> of depth 1 …</p><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap"><code>flatMap</code></a> method is identical to a <code>map</code> followed by a call to <code>flat</code> of depth 1.</p>
<p>This polyfill enables the function on Internet Explorer and some mobile browsers.</p>
<div class="highlight"><pre><span></span><code><span class="c1">// BEGIN polyfill_flatMap</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">flatMap</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">,</span><span class="w"> </span><span class="s1">'flatMap'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">value</span><span class="o">:</span><span class="w"> </span><span class="kd">function</span><span class="p">(</span><span class="nx">callback</span><span class="p">,</span><span class="w"> </span><span class="nx">thisArg</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">thisArg</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="k">this</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">self</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">TypeError</span><span class="p">(</span><span class="w"> </span><span class="s1">'Array.prototype.flatMap '</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="s1">'called on null or undefined'</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="ow">typeof</span><span class="w"> </span><span class="nx">callback</span><span class="w"> </span><span class="o">!==</span><span class="w"> </span><span class="s1">'function'</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">throw</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="ne">TypeError</span><span class="p">(</span><span class="w"> </span><span class="nx">callback</span><span class="w"> </span><span class="o">+</span><span class="w"></span>
<span class="w"> </span><span class="s1">' is not a function'</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[];</span><span class="w"></span>
<span class="w"> </span><span class="c1">// 1. Let O be ? ToObject(this value).</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">o</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Object</span><span class="p">(</span><span class="nx">self</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// 2. Let len be ? ToLength(? Get(O, "length")).</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">o</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">>>></span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kd">var</span><span class="w"> </span><span class="nx">k</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">0</span><span class="p">;</span><span class="w"> </span><span class="nx">k</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="nx">len</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="nx">k</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">k</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="nx">o</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">var</span><span class="w"> </span><span class="nx">part_list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">callback</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">self</span><span class="p">,</span><span class="w"> </span><span class="nx">o</span><span class="p">[</span><span class="nx">k</span><span class="p">],</span><span class="w"> </span><span class="nx">k</span><span class="p">,</span><span class="w"> </span><span class="nx">o</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="nx">list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">list</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">part_list</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">list</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">});</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="c1">// END polyfill_flatMap</span><span class="w"></span>
</code></pre></div>
<h1>Example</h1>
<p>After loading the polyfill this code works on all browsers:</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">nested_list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="mf">2</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="mf">3</span><span class="p">,</span><span class="w"> </span><span class="mf">4</span><span class="p">],</span><span class="w"> </span><span class="mf">5</span><span class="p">,</span><span class="w"> </span><span class="mf">6</span><span class="w"> </span><span class="p">];</span><span class="w"></span>
<span class="c1">// testing for the length field</span><span class="w"></span>
<span class="kd">var</span><span class="w"> </span><span class="nx">flat_list</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">nested_list</span><span class="p">.</span><span class="nx">flatMap</span><span class="p">(</span><span class="nx">x</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">x</span><span class="p">.</span><span class="nx">length</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="nx">x</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="nx">x</span><span class="p">]);</span><span class="w"></span>
<span class="c1">// Array(6) [ 1, 2, 3, 4, 5, 6 ]</span><span class="w"></span>
</code></pre></div>Rusts hidden talents speeding up builds and managing versions2019-06-07T00:00:00+02:002019-06-11T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-06-07:/2019/6/7/rusts-hidden-talents/<p>These tips are building on top of <a href="https://rustup.rs/">rustup</a> the official installer for the rust compiler and …</p><p>These tips are building on top of <a href="https://rustup.rs/">rustup</a> the official installer for the rust compiler and packet manager cargo.</p>
<h1>Rust version or toolchain per project</h1>
<p>The idea is to include the version of the toolchain used can be included in the project.
Allowing the developers of a project to upgrade the toolchain together with a simple <code>git pull</code>.
This feature was asked after people working on android projects came into contact with the gradle wrapper <code>gradlew</code>.</p>
<p>Setting a fixed version:</p>
<div class="highlight"><pre><span></span><code><span class="nb">echo</span> <span class="s2">"nightly-2019-01-21"</span> > rust-toolchain
<span class="nb">echo</span> <span class="s2">"1.34.0"</span> > rust-toolchain
</code></pre></div>
<p>Setting a named version:</p>
<div class="highlight"><pre><span></span><code><span class="nb">echo</span> <span class="s2">"nightly"</span> > rust-toolchain
<span class="nb">echo</span> <span class="s2">"beta"</span> > rust-toolchain
<span class="nb">echo</span> <span class="s2">"stable"</span> > rust-toolchain
</code></pre></div>
<p><strong>Note:</strong> if you choose <code>stable</code>, <code>beta</code> or <code>nightly</code> your project members will still have to run <code>rustup update</code> manually.</p>
<h2>Cleaning up</h2>
<p>After some time your machine amounts many different versions.</p>
<p>To remove them first list them:</p>
<div class="highlight"><pre><span></span><code>rustup toolchain list
</code></pre></div>
<p>Then remove them like this:</p>
<div class="highlight"><pre><span></span><code><span class="n">rustup</span><span class="w"> </span><span class="n">toolchain</span><span class="w"> </span><span class="n">uninstall</span><span class="w"> </span><span class="mf">1.9.0</span><span class="w"></span>
<span class="n">rustup</span><span class="w"> </span><span class="n">toolchain</span><span class="w"> </span><span class="n">uninstall</span><span class="w"> </span><span class="mf">1.13.0</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">unknown</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="w"></span>
<span class="n">rustup</span><span class="w"> </span><span class="n">toolchain</span><span class="w"> </span><span class="n">uninstall</span><span class="w"> </span><span class="n">beta</span><span class="w"></span>
</code></pre></div>
<h1>Global build cache</h1>
<p>This helps saving build time and energy however you need to know three important things:</p>
<ol>
<li>You must stay on the same compiler version on your machine for (almost all projects)</li>
<li>You can only build one project at the same time (one cargo command per user per machine)</li>
<li>Building binaries or projects with the same name will result in the last compiled binary with that name to be executed</li>
</ol>
<p>For setting this up you need to extend your shell's environment.</p>
<div class="highlight"><pre><span></span><code><span class="nb">export</span> <span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$HOME</span><span class="s2">/.cargo/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
<span class="nb">export</span> <span class="nv">CARGO_TARGET_DIR</span><span class="o">=</span>~/.cargo/build_cache/
</code></pre></div>
<h2>Overriding per project</h2>
<p>For some projects you will not want to use the global cache.
If you are already using <code>.env</code> you can unset the <code>CARGO_TARGET_DIR</code> variable there or do it manually:</p>
<div class="highlight"><pre><span></span><code><span class="nb">unset</span> CARGO_TARGET_DIR
</code></pre></div>
<p>You should document if you project breaks with a shared target directory or if your project hugely benefits from it.</p>
<p>Another approach is to use multiple shared target directories and group them by topic for example projects with actix.</p>
<h2>Alternative: sccache</h2>
<p>After publishing this on <a href="https://www.reddit.com/r/rust/comments/byz34r/rusts_hidden_talents_toolchain_and_build_speed/">reddit</a> I was pointed to <a href="https://github.com/mozilla/sccache">sccache</a> from Mozilla.</p>
<p>However their rust support is still marked <a href="https://github.com/mozilla/sccache/blob/master/docs/Rust.md">expremimental</a>.
There are <strong>some caveats</strong> with <code>env!</code>, macros, target specs and <strong>files read at compile time</strong>.</p>
<p>Reading has it with rust 1.18 or later you can enable sccache with the following commands:</p>
<div class="highlight"><pre><span></span><code>cargo install sccache
<span class="nb">export</span> <span class="nv">RUSTC_WRAPPER</span><span class="o">=</span>sccache
</code></pre></div>JKU schliesst Bioinformatik Master Studiengang -> Neu Artificial Intelligence2019-05-14T00:00:00+02:002019-05-14T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-05-14:/2019/5/14/jku-schliesst-bioinformatik-master-studiengang-neu-artificial-intelligence/<p>Die <a href="https://www.jku.at/">Johannes Kepler Universität</a> in Linz schliesst den Bioinformatik Studiengang in diesem Semester.
Ein Teil der …</p><p>Die <a href="https://www.jku.at/">Johannes Kepler Universität</a> in Linz schliesst den Bioinformatik Studiengang in diesem Semester.
Ein Teil der Kurse wird vermutlich in das <a href="https://www.jku.at/studium/studienarten/master/ma-artificial-intelligence/">Masterstudium
Artificial Intelligence</a> werden.</p>
<p>Ich habe das heute zufällig auf Umwegen von diesem Umstand erfahren.
Leider akzeptiert die Uni immer noch Anmeldungen für Bioinformatik.</p>
<p>Auch auf der Webseite findet sich (noch) kein Hinweis auf die Änderungen.</p>How to build a distributed secure application with Rust (Budapest Meetup)2019-05-06T00:00:00+02:002019-05-06T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-05-06:/2019/5/6/how-to-build-a-distributed-secure-application-with-rust-budapest-meetup/<p><a href="https://www.meetup.com/Rust-Hungary-Meetup/events/260651034/"><img alt="Flyer" src="https://estada.ch/2019/5/6/how-to-build-a-distributed-secure-application-with-rust-budapest-meetup/images/2019-05-06_talk-budapest-distributed-secure-application.png"></a></p>
<p>Stefan Schindler is a Software Engineer with interests ranging from small to big to embedded and …</p><p><a href="https://www.meetup.com/Rust-Hungary-Meetup/events/260651034/"><img alt="Flyer" src="https://estada.ch/2019/5/6/how-to-build-a-distributed-secure-application-with-rust-budapest-meetup/images/2019-05-06_talk-budapest-distributed-secure-application.png"></a></p>
<p>Stefan Schindler is a Software Engineer with interests ranging from small to big to embedded and distributed systems.
He is a core organizer of RustFest.eu and Rust Zürichsee gave workshops and talks about rust on two continents.</p>
<p>In this talk we will go into the building blocks of distributed systems.
How networks work, what abstractions are common, which assumptions drove their design and finally will influence today's applications when building safe and reliable applications.</p>
<p>Our example project will be a small to mid size client-master-slave application with full encryption.
So we will look at high level concepts and one concrete implementation.</p>
<p>Bring your laptop if you would like to play with the example project.</p>
<p>Thank you <a href="https://www.linkedin.com/in/zsuzsannaschleer/">Zsuzsanna</a> for hosting.</p>Video Parallel Programming in Amsterdam released2019-04-30T00:00:00+02:002019-04-30T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-04-30:/2019/4/30/video-parallel-programming-in-amsterdam-released/<p>Today the recording was released on the <a href="https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA">Rust Youtube Channel</a>:</p>
<p><a href="https://youtu.be/fi4hsbijVxU"><img alt="Youtube recording of "Parallel Programming in Amsterdam"" src="https://estada.ch/images/2019-03-20_youtube-banner.png"></a></p>
<p>The slides: <a href="https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/">https://estada.ch/2019 …</a></p><p>Today the recording was released on the <a href="https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA">Rust Youtube Channel</a>:</p>
<p><a href="https://youtu.be/fi4hsbijVxU"><img alt="Youtube recording of "Parallel Programming in Amsterdam"" src="https://estada.ch/images/2019-03-20_youtube-banner.png"></a></p>
<p>The slides: <a href="https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/">https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/</a></p>IPv6 only in Amsterdam - unable to build Rust2019-03-20T00:00:00+01:002019-03-21T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-03-20:/2019/3/20/ipv6-only-in-amsterdam-unable-to-build-rust/<p>Today I am sitting in the super friendly Latei with a good vegetarian quiche, a great …</p><p>Today I am sitting in the super friendly Latei with a good vegetarian quiche, a great tea, music in the background and a view into the street plus a 40Mbit/s high speed internet connection.
Preparing the slides for my presentation tonight I run every code sample again.</p>
<p>This works until cargo needs to download a crate:</p>
<div class="highlight"><pre><span></span><code> Unable to update registry <span class="sb">`</span>https://github.com/rust-lang/crates.io-index<span class="sb">`</span>
</code></pre></div>
<p><img alt="Speed test 40Mbit/s" src="https://estada.ch/2019/3/20/ipv6-only-in-amsterdam-unable-to-build-rust/images/2019-03-20_ipv6-only-speedtest.png"></p>
<h2>Update 2019-03-21</h2>
<p>I <a href="https://internals.rust-lang.org/t/unable-to-build-over-ipv6-only-requires-ipv4/9673">opened a thread on the internals forum</a>.</p>
<h1>What breaks?</h1>
<p>To be brief: building complex stuff needs these two</p>
<ul>
<li><a href="https://crates.io/">crates.io</a></li>
<li><a href="https://github.com/">github.com</a></li>
</ul>
<p>One crate I had locally was on <a href="https://gitlab.com/">gitlab.com</a>, but this is out of tree, so I can work around it.</p>
<h1>What works?</h1>
<ul>
<li><a href="https://docs.rs/">docs.rs</a></li>
<li><a href="https://doc.rust-lang.org/">doc.rust-lang.org</a></li>
<li><a href="https://www.rust-lang.org/">rust-lang.org</a></li>
<li><a href="https://rustup.rs/">rustup.rs</a></li>
<li><a href="https://users.rust-lang.org/">users.rust-lang.org</a> (except github login)</li>
<li><a href="https://internals.rust-lang.org/">internals.rust-lang.org</a> (except github login)</li>
</ul>
<p>So I can not complain about it because I can not login ;)</p>
<h1>What to do?</h1>
<h2>Infrastructure</h2>
<p>The easy solution would be to ask github to enable IPv6.
From what I can tell people complain since 2014 about that there appears to be no progress.</p>
<p>So the alternative would be to look for</p>
<ul>
<li>either a different platform to serve the content ourselves (interesting, but requires people)</li>
<li>or a reverse proxy solution with a CDN. (maybe costs money?)</li>
</ul>
<p>Both would have to be implemented with future cargo releases but the old releases could live on without major problems.</p>
<h2>Error message</h2>
<p>This error does not tell the relevant part about the lack of IPv4 connectivity.
Even worse, if they try google.com (what I expect 90% of the people to do) then they see the internet working so rust must be broken.</p>
<div class="highlight"><pre><span></span><code>cargo run
Updating crates.io index
warning: spurious network error <span class="o">(</span><span class="m">2</span> tries remaining<span class="o">)</span>: curl error:
<span class="p">;</span> <span class="nv">class</span><span class="o">=</span>Net <span class="o">(</span><span class="m">12</span><span class="o">)</span>
warning: spurious network error <span class="o">(</span><span class="m">1</span> tries remaining<span class="o">)</span>: curl error:
<span class="p">;</span> <span class="nv">class</span><span class="o">=</span>Net <span class="o">(</span><span class="m">12</span><span class="o">)</span>
error: failed to load <span class="nb">source</span> <span class="k">for</span> a dependency on <span class="sb">`</span>threadpool<span class="sb">`</span>
Caused by:
Unable to update registry <span class="sb">`</span>https://github.com/rust-lang/crates.io-index<span class="sb">`</span>
Caused by:
failed to fetch <span class="sb">`</span>https://github.com/rust-lang/crates.io-index<span class="sb">`</span>
Caused by:
curl error:
<span class="p">;</span> <span class="nv">class</span><span class="o">=</span>Net <span class="o">(</span><span class="m">12</span><span class="o">)</span>
</code></pre></div>
<h1>Workarounds</h1>
<p>First one could try a VPN or SSH <strong>but</strong> as with most public places the only allow TCP port 80 and 443 for HTTP and HTTPS traffic.</p>
<p>Another option could be to edit the local <code>/etc/hosts</code> file and have some reverse proxy handy.</p>
<p>At last one could think of a HTTP proxy service.
However most of them break the encryption, are mostly run by evil personnel and I can not recommend executing code that was transmitted without encryption or authentication over the internet.</p>
<h1>The situation has been improved over 2016</h1>
<p>After searching the forum I <a href="https://internals.rust-lang.org/t/ipv6-reachability-of-rust-websites/4379/3">found this old post</a> from 2016.
All of the services managed directly by the rust project are reachable over IPv6, thank you to the people that did set it up.</p>Parallel Programming in Amsterdam2019-03-20T00:00:00+01:002019-05-03T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-03-20:/2019/3/20/parallel-programming-in-amsterdam/<p>Tonight I will talk at the <a href="https://www.meetup.com/Rust-Amsterdam/events/259729119/">Rust Amsterdam meetup</a> about parallel programming.</p>
<p><a href="https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/slides/2019-03-20_parallel_programming-amsterdam.pdf"><img alt="Download Slides" src="https://estada.ch/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>
<p><a href="https://github.com/dns2utf8/thread-pools-and-iterators/tree/master/beispiele">The examples …</a></p><p>Tonight I will talk at the <a href="https://www.meetup.com/Rust-Amsterdam/events/259729119/">Rust Amsterdam meetup</a> about parallel programming.</p>
<p><a href="https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/slides/2019-03-20_parallel_programming-amsterdam.pdf"><img alt="Download Slides" src="https://estada.ch/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>
<p><a href="https://github.com/dns2utf8/thread-pools-and-iterators/tree/master/beispiele">The examples</a> are available on <a href="https://github.com/dns2utf8/thread-pools-and-iterators/tree/master/beispiele">my github</a>.</p>
<h1>The video recording</h1>
<p><a href="https://youtu.be/fi4hsbijVxU"><img alt="Youtube recording of "Parallel Programming in Amsterdam"" src="https://estada.ch/images/2019-03-20_youtube-banner.png"></a></p>
<p>Concurrent programming used to be hard. Stefan Schindler is talking about techniques to structure code using ThreadPool and Iterator with rayon.</p>
<p>This talk will teach us the basic building blocks to not get lost in a forest of threads and tasks.</p>
<ul>
<li>Slides: <a href="https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/">https://estada.ch/2019/3/20/parallel-programming-in-amsterdam/</a></li>
<li>Examples: <a href="https://github.com/dns2utf8/thread-pools-and-iterators/tree/master/beispiele">https://github.com/dns2utf8/thread-pools-and-iterators/tree/master/beispiele</a></li>
<li>ThreadPool: <a href="https://crates.io/crates/threadpool">https://crates.io/crates/threadpool</a></li>
<li>Rayon: <a href="https://crates.io/crates/rayon">https://crates.io/crates/rayon</a></li>
</ul>
<p>Questions start at <a href="https://youtu.be/fi4hsbijVxU?t=2466">41:06</a></p>How to setup DNSSEC with Knot for your domain2019-03-10T00:00:00+01:002019-10-30T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-03-10:/2019/3/10/how-to-setup-dnssec-with-knot-for-your-domain/<p>A step by step tutorial on how enable DNSSEC for your domain with the <a href="https://www.knot-dns.cz/">Knot DNS …</a></p><p>A step by step tutorial on how enable DNSSEC for your domain with the <a href="https://www.knot-dns.cz/">Knot DNS server</a> version <a href="https://www.knot-dns.cz/2019-03-05-version-280.html">2.8.0 released</a> last week on 2019-03-05.</p>
<p>I am going to assume you:</p>
<ul>
<li>Have control over a <a href="https://en.wikipedia.org/wiki/Second-level_domain">second level domain</a> like <code>estada.ch</code></li>
<li>Have a registrar that supports DNSSEC, see the last step to check this.</li>
<li>Have a machine to run Knot on, can be a virtual machine (search for VPS and your country)<ul>
<li>Pick a name for your machine eg. <code>ns1.estada.ch</code></li>
<li>Public and static IPs<ul>
<li>For IPv4 this means any IP outside of <a href="https://tools.ietf.org/html/rfc1918">RFC 1918</a></li>
<li>For IPv6 this means any IP starting with <a href="https://docs.oracle.com/cd/E19253-01/816-4554/ipv6-overview-170/index.html"><code>2001::/16</code></a></li>
</ul>
</li>
<li>Configure your assigned IPs and make sure that port 53 UDP and TCP are reachable and no firewall is blocking traffic</li>
</ul>
</li>
<li>Have the latest <a href="https://www.debian.org/">Debian 9 stable</a> installed on that machine</li>
<li>If you plan to run Knot remotely:<ul>
<li><a href="https://wiki.debian.org/SSH">OpenSSH</a> configured on the server</li>
<li>and the <code>openssh-client</code> on your machine</li>
</ul>
</li>
<li>Know how to become root (eg. <a href="https://wiki.debian.org/sudo">with sudo</a>)</li>
<li>Have access to the <a href="https://www.knot-dns.cz/docs/2.8/singlehtml/index.html">Knot DNS Manual</a></li>
</ul>
<p>For simplicity I use <code>estada.ch</code> as the example in the whole article.</p>
<h1>Configure your machine as name server (Glue records)</h1>
<p>Write an email or open a support ticket at your registrar to supply your machine as name server.</p>
<p>Request that they register your machine <code>ns1.estada.ch</code> with your addresses eg. <code>2a0a:51c0::61</code> and <code>185.194.239.73</code>.</p>
<p><strong>Note</strong> this manual process usually takes some time sometimes a business day.</p>
<h1>Configure the public IPs</h1>
<p><a href="https://wiki.debian.org/NetworkConfiguration">Debian Network Configuration</a> on servers is usually done with <code>/etc/network/interfaces</code>.</p>
<p>For other distributions you best search the internet.</p>
<h1>Install Knot</h1>
<p>Add the source of the Knot software to by creating this file <code>/etc/apt/sources.list.d/knot.list</code> with that content:</p>
<div class="highlight"><pre><span></span><code><span class="k">deb</span><span class="w"> </span><span class="s">https://deb.knot-dns.cz/knot-latest/</span><span class="w"> </span><span class="kp">stretch</span><span class="w"> </span><span class="kp">main</span><span class="w"></span>
</code></pre></div>
<p>Then save the certificate to the system:</p>
<div class="highlight"><pre><span></span><code>curl https://deb.knot-dns.cz/knot-latest/apt.gpg > /etc/apt/trusted.gpg.d/knot-latest.gpg
</code></pre></div>
<p>And install the dependencies and the package:</p>
<div class="highlight"><pre><span></span><code>apt install apt-transport-https
apt update <span class="o">&&</span> apt upgrade
apt install knot
</code></pre></div>
<h2>Configure Knot to load the zone</h2>
<p>Create the file <code>/etc/knot/knot.conf</code> with the following contents:</p>
<div class="highlight"><pre><span></span><code><span class="c1">#</span><span class="w"></span>
<span class="c1"># This is a sample of a minimal configuration file for Knot DNS.</span><span class="w"></span>
<span class="c1"># For more details, see man 5 knot.conf or refer to the server documentation.</span><span class="w"></span>
<span class="c1">#</span><span class="w"></span>
<span class="nt">server</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Listen on all configured IPv4 interfaces.</span><span class="w"></span>
<span class="w"> </span><span class="nt">listen</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">0.0.0.0@53</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Listen on all configured IPv6 interfaces.</span><span class="w"></span>
<span class="w"> </span><span class="nt">listen</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">::@53</span><span class="w"></span>
<span class="w"> </span><span class="c1"># User for running the server.</span><span class="w"></span>
<span class="w"> </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">knot:knot</span><span class="w"></span>
<span class="w"> </span><span class="nt">rundir</span><span class="p">:</span><span class="w"> </span><span class="s">"/run/knot"</span><span class="w"></span>
<span class="nt">mod-rrl</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">default</span><span class="w"></span>
<span class="w"> </span><span class="nt">rate-limit</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">200</span><span class="w"> </span><span class="c1"># Allow 200 resp/s for each flow</span><span class="w"></span>
<span class="w"> </span><span class="nt">slip</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">2</span><span class="w"> </span><span class="c1"># Every other response slips</span><span class="w"></span>
<span class="nt">log</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Log info and more serious events to syslog.</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">target</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">syslog</span><span class="w"></span>
<span class="w"> </span><span class="nt">any</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">info</span><span class="w"></span>
<span class="nt">policy</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">rsa2k</span><span class="w"></span>
<span class="w"> </span><span class="nt">algorithm</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">RSASHA256</span><span class="w"></span>
<span class="w"> </span><span class="nt">ksk-size</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">4096</span><span class="w"></span>
<span class="w"> </span><span class="nt">zsk-size</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">2048</span><span class="w"></span>
<span class="w"> </span><span class="nt">nsec3</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">on</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ececc</span><span class="w"></span>
<span class="w"> </span><span class="nt">algorithm</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ecdsap384sha384</span><span class="w"></span>
<span class="w"> </span><span class="nt">nsec3</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">on</span><span class="w"></span>
<span class="nt">template</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">default</span><span class="w"></span>
<span class="w"> </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="s">"/var/lib/knot"</span><span class="w"></span>
<span class="w"> </span><span class="nt">dnssec-signing</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">on</span><span class="w"></span>
<span class="w"> </span><span class="nt">dnssec-policy</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">rsa2k</span><span class="w"></span>
<span class="w"> </span><span class="nt">global-module</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">mod-rrl/default</span><span class="w"> </span><span class="c1"># Enable RRL globally</span><span class="w"></span>
<span class="nt">zone</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Master zone.</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">domain</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">estada.ch</span><span class="w"></span>
<span class="w"> </span><span class="nt">file</span><span class="p">:</span><span class="w"> </span><span class="s">"/etc/knot/estada.ch.zone"</span><span class="w"></span>
<span class="nt">zone</span><span class="p">:</span><span class="w"></span>
<span class="w"> </span><span class="c1"># Some other zone</span><span class="w"></span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">domain</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">somethingelse.ch</span><span class="w"></span>
<span class="w"> </span><span class="nt">file</span><span class="p">:</span><span class="w"> </span><span class="s">"/etc/knot/somethingelse.ch.zone"</span><span class="w"></span>
</code></pre></div>
<p>The <code>rate-limit</code> is to prevent your DNS server from being misused in an <a href="https://www.incapsula.com/ddos/attack-glossary/dns-amplification.html">DNS amplification attack</a>.</p>
<p><strong>Note</strong> your server is in the role of the primary resolver.
That means most of your legitimate users will use their <a href="https://en.wikipedia.org/wiki/Internet_service_provider">ISP's</a> DNS as an intermediate and</p>
<h1>Configure your zone</h1>
<p>To configure DNS you must supply the DNS server Knot with a <a href="https://www.knot-dns.cz/docs/2.8/html/man_kzonecheck.html">zone file</a>.
So for <a href="https://www.ripe.net/publications/docs/ripe-203">example</a> the start of my zone currently looks like this:</p>
<div class="highlight"><pre><span></span><code>estada.ch. 86400 SOA ns1.estada.ch. dnsadmin.estada.ch. 2019020203 14400 600 2419200 600
estada.ch. 86400 NS ns0.estada.ch.
estada.ch. 86400 NS ns1.estada.ch.
estada.ch. 86400 AAAA 2a0a:51c0::61
estada.ch. 86400 A 185.194.239.73
</code></pre></div>
<p>The <a href="https://support.dnsimple.com/articles/soa-record/">SOA line</a> describes the following fields:</p>
<ol>
<li>primary name server</li>
<li>email of the zone administrator</li>
<li>serial with the last updated date encoded plus a two digit counter, <strong>increase with every edit</strong></li>
<li>refresh every <code>14400</code> seconds</li>
<li>retry every <code>600</code> seconds</li>
<li>expire after <code>2419200</code> seconds</li>
<li>minimum delay retry for <code>600</code> seconds for negative results</li>
</ol>
<p><strong>Note</strong> that the Knot daemon will change this file and extend it with the hashes and <a href="https://tools.ietf.org/html/rfc5155">NSEC3</a> records.</p>
<h1>Get you DNSKEY id 257</h1>
<p>After you reloaded the Knot service with this command:</p>
<div class="highlight"><pre><span></span><code>sudo systemctl reload knot
</code></pre></div>
<p>You can get the public part of your zone file with this command:</p>
<div class="highlight"><pre><span></span><code>sudo keymgr estada.ch dnskey
</code></pre></div>
<p>If you need the <code>DS</code> form of the key run run this command:</p>
<div class="highlight"><pre><span></span><code>sudo keymgr estada.ch ds
</code></pre></div>
<h1>Publish on your registrar</h1>
<p>After logging in to the control panel of your registrar, search for the DNS configuration option of your domain.</p>
<p><img alt="Step one" src="/images/2019-03-10_registrar-ksk-0.png"></p>
<p>Then to enter your new KSK-Hash paste all currently valid entries in the form.</p>
<p><img alt="Step two" src="/images/2019-03-10_registrar-ksk-1.png"></p>
<p>I use a special setup with two chains, so I have to enter two KSK hashes.</p>
<h2>Test the publication with dnsviz.net</h2>
<p>The tool <a href="http://dnsviz.net">dnsviz.net</a> is a very popular graphical tool.</p>
<p>To check the configuration of <code>estada.ch</code> we can use this link:
<a href="http://dnsviz.net/d/estada.ch/dnssec/">http://dnsviz.net/d/estada.ch/dnssec/</a></p>
<p>If everything worked, you should see your chain terminate like the left part in this picture:
<img alt="estada.ch secured with DNSSEC" src="https://estada.ch/2019/3/10/how-to-setup-dnssec-with-knot-for-your-domain/images/2019-03-10_estada.ch-secured.png"></p>
<h2>Test the publication by hand</h2>
<p>Compare the output of the <code>DS</code> generated by the Knot DNS server with this command:</p>
<div class="highlight"><pre><span></span><code>sudo keymgr estada.ch ds
</code></pre></div>
<p>Search for the last three parts of the output with the output of the <strong>kdig</strong> command from the <a href="https://packages.debian.org/stretch-backports/knot-dnsutils">knot-dnsutils</a> or the <a href="https://linux.die.net/man/1/dig">dig command</a> from the <a href="https://packages.debian.org/stretch-backports/dnsutils">dnsutils</a> package.</p>
<div class="highlight"><pre><span></span><code>kdig ANY @a.nic.ch estada.ch +dnssec
</code></pre></div>
<p><strong>Important</strong> if you are using the <code>dig</code> you must remove the space seperating the last 8 characters like this:</p>
<div class="highlight"><pre><span></span><code><span class="mf">8</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="mf">28521</span><span class="n">FE33926668A3D12B6212DA003226CAAD44430D6355F990BAAAD</span><span class="w"> </span><span class="mf">8</span><span class="n">EAB2B3E</span><span class="w"></span>
<span class="n">X</span><span class="w"> </span><span class="n">Y</span><span class="w"> </span><span class="o">>------------------------</span><span class="w"> </span><span class="c1">remove space here ------------^</span>
</code></pre></div>
<p><strong>Notice</strong> that the server responsible for the <code>.ch</code> domains is <code>a.nic.ch</code>.
If your second level domain does not belong to <code>.ch</code> you need to lookup the primary primary server.</p>
<h2>What if my registrar does not support DNSSEC?</h2>
<p>First you should ask the support team of your provider.
Maybe their interface is hidden or even via email.</p>
<p>Sadly some companies like metanet to this day do not support DNSSEC and your only option is to transfer the domain to another registrar like cyon or hosttech.</p>
<h2>Switch just broke my setup with CDS (Child DS)</h2>
<p>Switch has enabled the new feature CDS (Child DS).
The goal was to simplify DNSSec provisioning, but it breaks existing setups with two independent ksk-chains.</p>
<p>You can <a href="https://www.nic.ch/faqs/dnssec/cds/">test your domain with their new tool</a>.</p>
<h2>I need more help</h2>
<p>Zeroth, test your setup with tools like <a href="http://dnsviz.net">dnsviz</a> or <a href="https://mxtoolbox.com/SuperTool.aspx?action=soa%3aestada.ch&run=toolpage">MX Toolbox</a>.</p>
<p>First, ask a friend or two.
DNS is an old topic, so it has grown over time and sometimes the complexity is no longer obvious to people who did work those problems for a long time.</p>
<p>Second, read more documentation or reach out to strangers.
If you ask nicely and lead with the work you already invested people tend to help you on the internet.</p>Server security update2019-02-28T00:00:00+01:002019-02-28T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-02-28:/2019/2/28/server-security-update/<p>Since security is a process not a single task I updated my server config.</p>
<p>I noticed …</p><p>Since security is a process not a single task I updated my server config.</p>
<p>I noticed that I was missing a good dhparam.
This is a follow up post to <a href="/2018/11/12/caa-site-configuration-security-and-http2/">last years "CAA, site configuration, security and HTTP2" post</a>.</p>
<h1>Generate the prime</h1>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">ssl_ecdh_curve</span><span class="w"> </span><span class="s">secp384r1</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">ssl_dhparam</span><span class="w"> </span><span class="s">/etc/nginx/dhparam.pem</span><span class="p">;</span><span class="w"></span>
</code></pre></div>
<h2>Disable prime256v1 aka NIST P-256</h2>
<p>After some searching on the internet I found some evidence about <a href="https://safecurves.cr.yp.to/">it beeing insecure and also known as "NIST P-256"</a>.
If you prefere a discussion <a href="https://github.com/nodejs/node/issues/1495">read the node.js tracker</a>.</p>
<p>Going for the stronger curve <a href="https://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use/78624#78624">should have a minimal impact on performance</a>.</p>
<h1>Regenerate the dhparam</h1>
<p>Regenerating keys is a good idea in general.
<a href="https://security.stackexchange.com/questions/116479/regenerating-dhparam-periodically">In 2016 some stackexchange user said no</a>.</p>
<p>Automating it every week or so still seems like a good idea so we add this cron job with <code>sudo crontab -e</code>:</p>
<div class="highlight"><pre><span></span><code># m h dom mon dow command
14 3 * * 1 openssl dhparam -out /etc/nginx/dhparam.pem 4096 && chmod 600 /etc/nginx/dhparam.pem && /bin/systemctl restart nginx.service
</code></pre></div>
<p><strong>Note:</strong> We have to use absolute paths. You can get that absolute path with this command: <code>which ${binary}</code></p>
<p>See the <a href="https://manpages.debian.org/stretch/cron/crontab.5.en.html">crontab man page</a>.</p>Rust docs - A practical guide to making good documentation - Rust Zürichsee Meetup2019-02-18T00:00:00+01:002019-02-18T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-02-18:/2019/2/18/rust-docs-a-practical-guide-to-making-good-documentation-rust-zurichsee-meetup/<p>Today we are very excited to have <a href="https://twitter.com/baitman">Beth Aitman</a> give a practical guide to making good …</p><p>Today we are very excited to have <a href="https://twitter.com/baitman">Beth Aitman</a> give a practical guide to making good documentation in Rust (and other programming languages)!</p>
<p>The goal of this talk is to make it much less painful for you to write documentation - and possibly even enjoyable. She will give you a practical guide to writing effective documentation, and the conceptual background you need to understand the problem space.</p>
<p>We'll cover:</p>
<ul>
<li>what to write, and how to work out what you need to write</li>
<li>tips for making your writing clear and easy-to-read</li>
<li>making docs work for your audience (aka - when it is and isn't appropriate to talk about category theory)</li>
<li>maintenance and testing</li>
</ul>
<p>with plenty of examples of good and bad along the way.</p>
<p>You can signup <a href="https://www.meetup.com/Rust-Zurich/events/257893384/">on our meetup page</a>.</p>Upgrade certbot on Debian 9 stable [Updated 2019-01-28]2019-01-23T00:00:00+01:002019-01-28T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-01-23:/2019/1/23/upgrade-certbot-on-debian-9-stable/<p>Since Debian stretch was <a href="https://www.debian.org/releases/">released 2017-06-17</a> it was updated six times to 9.6 on 2018-11-10 …</p><p>Since Debian stretch was <a href="https://www.debian.org/releases/">released 2017-06-17</a> it was updated six times to 9.6 on 2018-11-10.</p>
<h1>Update 2019-01-28</h1>
<p>Over the weekend the debian team released the update to the normal <code>stable-updates</code> channel.</p>
<p>To resume to the normal update path run these commands:</p>
<div class="highlight"><pre><span></span><code>sudo apt update <span class="o">&&</span> sudo apt upgrade <span class="o">&&</span> sudo apt autoremove
sudo apt dist-upgrade
</code></pre></div>
<p>To cleanup the backports run:</p>
<div class="highlight"><pre><span></span><code>sudo rm /etc/apt/sources.list.d/stretch-backports.list
</code></pre></div>
<h1>Backports</h1>
<p>For <a href="https://certbot.eff.org/">certbot</a> this means that its version was frozen two years ago.
Since then there have been <a href="https://labs.detectify.com/2018/01/12/how-i-exploited-acme-tls-sni-01-issuing-lets-encrypt-ssl-certs-for-any-domain-using-shared-hosting/">problems with the TLS-SNI-01 method</a> so it will be removed in February 2019.</p>
<p>A newer version of certbot is available in debian backports and here is how to enable it.</p>
<h1>Applied how to <a href="https://backports.debian.org/Instructions/">based on general instructions</a></h1>
<p>First we will add support for tls to apt:</p>
<div class="highlight"><pre><span></span><code>sudo apt install apt-transport-https
</code></pre></div>
<p>Then we will add the backports repo to the system:</p>
<div class="highlight"><pre><span></span><code><span class="nb">echo</span> <span class="s2">"deb https://debian.ethz.ch/debian stretch-backports main contrib"</span> <span class="p">|</span> sudo tee /etc/apt/sources.list.d/stretch-backports.list
</code></pre></div>
<p>After that we have to fetch the list of available packages:</p>
<div class="highlight"><pre><span></span><code>sudo apt update
</code></pre></div>
<p>Finally we can update from the main package to the backports package:</p>
<div class="highlight"><pre><span></span><code>sudo apt install -t stretch-backports certbot
</code></pre></div>Rust 2019 wish list2019-01-09T00:00:00+01:002019-01-09T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-01-09:/2019/1/9/rust-2019-wish-list/<p>As with any wish list, it started short and then grew a bit.</p>
<p>There are three …</p><p>As with any wish list, it started short and then grew a bit.</p>
<p>There are three sections Community, Technical and Ethics and I hope it is not too lengthy.</p>
<p><img alt="Rust 2019 wish list" src="https://estada.ch/2019/1/9/rust-2019-wish-list/images/2018-01-09_wish-list.png"></p>
<h1>Community</h1>
<h2>Rust Family</h2>
<p>Have a discussion towards the goals of a Rust Family group.
What it should do and not do.</p>
<p>One goal could be to reduce the load of the people organizing the conferences.
How would we do that?</p>
<p>Maybe we can have enough people organizing so that one single person has to organize one conference per year and can visit another one in the same time.
During that time one would be available to help others on small tasks on short notice.</p>
<h3>Small Winter or Spring Retreat</h3>
<p>In the RustFest team we were talking about having a physical meet after the last conference.
If you were interested please email (@dns2utf8 at this domain) me so we can do an event with maybe 20 to 30 people.</p>
<p>If this hits a nerve we could grow it into an annual event that maybe also travels around or is run in parallel.
As soon as it is run in parallel we can then start thinking about stuff like having a video synchronized wall to virtually connect two or more spaces.</p>
<h3>Camp in Summer</h3>
<p>I would love to have a place to meet other Rust developers and talk in a impl days like format.</p>
<p>One possibility is to attend <a href="https://events.ccc.de/category/camp/">CCC Camp</a> see <a href="https://events.ccc.de/camp/2015/wiki/Main_Page">the 2015 wiki</a>.</p>
<p>Another would be to have our own event.
This way would have the advantage that we could offer a SOS-Stage (self organized sessions) that we could fill with bands in the evening.</p>
<h2>Survey and Engagement</h2>
<p>On of the pain points that people told me at 35c3 was this.
They felt very far away from the development of the language and at the same time did not feel the need to be included very much.
When I told them about the annual user survey the echo of 50% of people attending the workshop "Rust in the year 2025" was: What survey?
I would like to give it more attention in the following months.</p>
<p>There were many good points, <a href="/2018/12/27/rust-in-the-year-2025/">see the protocol here</a>.
My personal favorites are the following:</p>
<ul>
<li>Keep Rust focused<ul>
<li>Set non-goals</li>
<li>limit scope and keep it learn-able to new users</li>
</ul>
</li>
<li>General feedback is hard<ul>
<li>RFCs<ul>
<li>if known: too time consuming</li>
<li>not known: to new people, this may not be a problem</li>
</ul>
</li>
<li>Surveys - not known to more than 50%</li>
</ul>
</li>
<li>Variadic tuples</li>
<li>Const generics and functions</li>
</ul>
<h1>Technical</h1>
<h2>recognize loop breaking return</h2>
<p>The compiler should learn about the case where an early return prevents use after move.</p>
<div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">ask_user</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">query</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">fallback</span>: <span class="nb">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">)</span><span class="w"> </span>-> <span class="nc">T</span><span class="w"></span>
<span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">T</span>: <span class="nc">std</span>::<span class="n">fmt</span>::<span class="n">Debug</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">std</span>::<span class="kt">str</span>::<span class="n">FromStr</span><span class="w"></span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">T</span>::<span class="nb">Err</span>: <span class="nc">std</span>::<span class="n">fmt</span>::<span class="n">Debug</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">query</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">fallback</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">fallback</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">format!</span><span class="p">(</span><span class="s">"{} [default: {:?}]: "</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">,</span><span class="w"> </span><span class="n">fallback</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">format!</span><span class="p">(</span><span class="s">"{}: "</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">stdout</span><span class="p">().</span><span class="n">flush</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">"unable to flush stdout"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">stdin</span><span class="p">().</span><span class="n">read_line</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">"unable to read from stdin"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">trim</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">fallback</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">fallback</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">*</span><span class="n">fallback</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">parse</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">" (EE) input error {:?}"</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>Currently the error message points to <code>rustc --explain E0507</code>.
The explanation talks about RefCell and it's usage, not loops and returns.</p>
<p>After talking to Mutabah on IRC we found the problem: the unconditional move.
So flipping the checks <code>if input == ""</code> and <code>if let Some(fallback) = fallback</code> solves the use after move problem.</p>
<div class="highlight"><pre><span></span><code><span class="k">fn</span> <span class="nf">ask_user</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">(</span><span class="n">query</span>: <span class="kp">&</span><span class="kt">str</span><span class="p">,</span><span class="w"> </span><span class="n">fallback</span>: <span class="nb">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">)</span><span class="w"> </span>-> <span class="nc">T</span><span class="w"></span>
<span class="w"> </span><span class="k">where</span><span class="w"> </span><span class="n">T</span>: <span class="nc">std</span>::<span class="n">fmt</span>::<span class="n">Debug</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">std</span>::<span class="kt">str</span>::<span class="n">FromStr</span><span class="w"></span>
<span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">T</span>::<span class="nb">Err</span>: <span class="nc">std</span>::<span class="n">fmt</span>::<span class="n">Debug</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">query</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">fallback</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&</span><span class="n">fallback</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">format!</span><span class="p">(</span><span class="s">"{} [default: {:?}]: "</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">,</span><span class="w"> </span><span class="n">fallback</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">format!</span><span class="p">(</span><span class="s">"{}: "</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">String</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span><span class="w"> </span><span class="n">query</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">stdout</span><span class="p">().</span><span class="n">flush</span><span class="p">().</span><span class="n">expect</span><span class="p">(</span><span class="s">"unable to flush stdout"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">clear</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="n">std</span>::<span class="n">io</span>::<span class="n">stdin</span><span class="p">().</span><span class="n">read_line</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span><span class="w"> </span><span class="n">input</span><span class="p">).</span><span class="n">expect</span><span class="p">(</span><span class="s">"unable to read from stdin"</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">trim</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="s">""</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">fallback</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fallback</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">fallback</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">input</span><span class="p">.</span><span class="n">parse</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nb">Ok</span><span class="p">(</span><span class="n">r</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">r</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="nb">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="w"> </span><span class="o">=></span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">" (EE) input error {:?}"</span><span class="p">,</span><span class="w"> </span><span class="n">e</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h2>Offline Support</h2>
<p>Have a documented cargo flag to run offline.</p>
<p>As a bonus, cargo could ask the operating system or it's services like NetworkManager if there is a connection at all.</p>
<h2>A Grammar</h2>
<p>Find the time to implement a complete grammar for rust and test it with rustc.</p>
<h1>Ethics</h1>
<p>To make it short.
There are ethics in the field of Rust.</p>
<p>Rust like any technology is sometimes good and sometimes bad.
We should start thinking about the idea of areas.
Areas where we do not want to contribute to progress and others where we see a need for progress that we are currently unable to work on.</p>
<p>I wish for next year to have more majored ideas than “This is interesting so I will spend a weekend on it” or “I really need this right now so I will mix up something quick”.</p>
<h2>Documentation as Enlightenment</h2>
<p>Make more crates usable and accessible by keeping everything documented on this high level or higher.</p>
<p>I wish for more people to lift others onto the shoulders of giants.</p>
<h2>Summary</h2>
<p>Rust enables us to do more with computers with less overhead and therefore less energy.
It would be a shame if we lost these energy savings to the <a href="https://en.wikipedia.org/wiki/Rebound_effect_(conservation)">rebound effect</a>.</p>
<p>In that regard I wish for the ability to have automatic fallbacks in crates or the compiler if the running machine does not support more recent feature.</p>
<p>To summarize there is a lot to do.
And we should celebrate milestones or kilometer-stones as we will say in the future.
Prepare something for Rust's fourth birthday.</p>January Meetup - Community Space Rust Embedded Edition 2018 - Rust Zürichsee2019-01-08T00:00:00+01:002019-01-08T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2019-01-08:/2019/1/8/january-meetup-community-space-rust-embedded-edition-2018-rust-zurichsee/<p>This month we will have an open space and time to talk hosted by <a href="https://www.coredump.ch/">coredump</a>.
Raphael …</p><p>This month we will have an open space and time to talk hosted by <a href="https://www.coredump.ch/">coredump</a>.
Raphael gave a <a href="/2018/12/11/rust-embedded-edition-2018-rust-zurichsee/">recap of his embedded update talk</a>.</p>revfs lightning talk at 35c3 (video)2018-12-30T00:00:00+01:002018-12-30T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-12-30:/2018/12/30/revfs-lightning-talk-at-35c3-video/<p><a href="https://media.ccc.de/v/35c3-9568-lightning_talks_day_4#t=1936">The recording of lightning talks on day 4</a> was published and you can find my part …</p><p><a href="https://media.ccc.de/v/35c3-9568-lightning_talks_day_4#t=1936">The recording of lightning talks on day 4</a> was published and you can find my part at <a href="https://media.ccc.de/v/35c3-9568-lightning_talks_day_4#t=1936">32:20</a>.</p>
<p>The source code is published <a href="https://gitlab.com/dns2utf8/revfs">on Gitlab</a>.</p>Rust in the year 20252018-12-27T00:00:00+01:002018-12-27T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-12-27:/2018/12/27/rust-in-the-year-2025/<h1>Summary protocol of the workshop "<a href="https://events.ccc.de/congress/2018/wiki/index.php/Session:Rust_2025">Rust in the year 2025</a>"</h1>
<ul>
<li>Types<ul>
<li>Refinement - Types with more predicates …</li></ul></li></ul><h1>Summary protocol of the workshop "<a href="https://events.ccc.de/congress/2018/wiki/index.php/Session:Rust_2025">Rust in the year 2025</a>"</h1>
<ul>
<li>Types<ul>
<li>Refinement - Types with more predicates</li>
<li>Variant types - only accept one variant of an <code>enum</code></li>
</ul>
</li>
<li>Formal verification</li>
<li>Smaller binaries</li>
<li>Steps towards a "stable" ABI</li>
<li>Capabilites oriented IO (like CloudABI)</li>
<li><code>#[no_panic]</code> annotation for very safe functions/modules/crates<ul>
<li>A lint to detect panics with <code>#[deny(maybe_panic)]</code></li>
</ul>
</li>
<li>Better UI toolkit integration or self-hosted</li>
<li>Variadic touples</li>
<li>General feedback<ul>
<li>RFCs - not known or too time consuming</li>
<li>Surveys - not known to more than 50%</li>
</ul>
</li>
<li>Not overload the rust<ul>
<li>limit scope and keep it learnable to new users</li>
<li>Set non-goals</li>
<li>limit the number of language features</li>
</ul>
</li>
<li>More type information inside macros</li>
<li>Externalizs extensions from the language with DSLs - Anti Pattern</li>
<li>Const generics and functions</li>
<li>Macros should be able to generate uniqe names</li>
</ul>revfs lightning talk accpeted at 35c32018-12-26T00:00:00+01:002018-12-26T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-12-26:/2018/12/26/revfs-lightning-talk-accpeted-at-35c3/<p>revfs - export a local directory via ssh.
Learn more at <a href="https://c3lt.de/35c3/talk/AAJ97M/">2018-12-30, 12:05–12:10</a> in …</p><p>revfs - export a local directory via ssh.
Learn more at <a href="https://c3lt.de/35c3/talk/AAJ97M/">2018-12-30, 12:05–12:10</a> in the <a href="https://35c3.c3nav.de/l/hall-b/">room Borg</a>.</p>
<p>Some people know sshfs where you can export a remote folder locally. I would like to show how the reverse is useful and how far my OSS prototype is.</p>
<p>The source code will be published <a href="https://gitlab.com/dns2utf8/revfs">on Gitlab</a>.</p>Firefox privacy enhancement renders every off screen canvas white2018-12-22T00:00:00+01:002018-12-22T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-12-22:/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/<p>Some time ago I found a privacy enhancement guide like <a href="https://restoreprivacy.com/firefox-privacy/">this one</a> and followed some of …</p><p>Some time ago I found a privacy enhancement guide like <a href="https://restoreprivacy.com/firefox-privacy/">this one</a> and followed some of the recommendations.
One of them was set <code>privacy.resistFingerprinting</code> to <code>true</code>.</p>
<p>Once I toggled the switch there was a dialog asking me if I was interested in enabling additional security measures.</p>
<h3>Update: <a href="/2020/7/11/detecting-private-mode-in-2020/">Detecting private mode in 2020</a></h3>
<h1>Noticing some changes</h1>
<h2>reCAPTCHA</h2>
<p>After that I continued with my life for about a month where I was no longer able to login to <a href="https://www.digitec.ch/">Digitec</a>.
The reason was their reCAPTCHA v3 check.</p>
<p><a href="https://www.google.com/recaptcha/intro/v3.html#">Googles reCAPTCHA v3</a> does not work well with such a tight which is not nice but a choice they made.
However if it is broken they should tell the users instead of letting them solve puzzle after puzzle.</p>
<h2>Time zones</h2>
<p>The other odd I noticed was the time displayed at <a href="https://rustfest.estada.ch/timetable.html">RustFests Infobeamer</a> because it was off by an hour.
After some debugging I found that the browser had set the timezone to UTC-0.</p>
<h2>Background on this website no longer rendered</h2>
<p>When updating the page with some slides with Firefox 63 the background was white.
My first assumption was: the alpha channel is broken.</p>
<p>Testing that theory with another browser profile revealed a bigger mystery.</p>
<h1>What else was changed?</h1>
<p>Vaguely remembering that I agreed on some dialog to improve privacy I started searching in <code>about:config</code>.
After some time I found the following:</p>
<ul>
<li>app.normandy.user_id;XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX<ul>
<li>As of finishing up this article I am not 100% sure this setting is related.</li>
</ul>
</li>
<li>javascript.use_us_english_locale</li>
<li>extensions.ui.locale.hidden</li>
<li>privacy.spoof_english;2</li>
<li>privacy.resistFingerprinting;true</li>
</ul>
<p>For relevance we will take a look at each one of them in reverse order:</p>
<h1>privacy.resistFingerprinting</h1>
<p>First let's look at some pictures.
The colors have to following meaning:</p>
<ul>
<li><strong>Green:</strong> cavas element in the DOM directly rendered.</li>
<li><strong>Yellow:</strong> extracting the finished image data from the green tile and use it with <code>style.backgroundImage = 'url('+ green_canvas.toDataURL('image/png') +')';</code>.</li>
<li><strong>Red:</strong> use an off-screen canvas element and extract with <code>toDataURL</code> and use as <code>backgroundImage</code>.</li>
<li><strong>Violet:</strong> reuse the red canvas in the DOM with CSS3 <code>background-image: -moz-element(#canvas_green)</code>. I also tried to make it work with <code>-webkit-canvas(canvas_green)</code> however that was <a href="https://chromium.googlesource.com/chromium/src.git/+/24e58b0d8fab28fa20e69f191d833fe97aaff75d">removed from Chromium in 2015 already</a> but for some reason search engines still present it as a solution.</li>
</ul>
<h2>Live render in your browser</h2>
<div id="chessboard">
<!-- 1 -->
<canvas id="resistFingerprintingTest0"></canvas>
<!-- 2 -->
<canvas id="resistFingerprintingTest1"></canvas>
<!-- 3 -->
<div id="resistFingerprintingTestBackground0"></div>
<!-- 4 -->
<div id="resistFingerprintingTestBackground1"></div>
</div>
<p><link rel="stylesheet" type="text/css" href="https://estada.ch/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/2018-12-22_firefox_white_canvas.css" /></p>
<script src="https://estada.ch/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/2018-12-22_firefox_white_canvas.js"></script>
<p>This option came from the <a href="https://www.torproject.org/projects/torbrowser.html.en">TorBrowser</a> and is <a href="https://wiki.mozilla.org/Security/Fingerprinting">documented today</a>.</p>
<p>If you would like to use background images for some fancy background, consider this detection code:</p>
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">16</span><span class="p">;</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">16</span><span class="p">;</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">canvas</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s1">'canvas'</span><span class="p">);</span><span class="w"></span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">width</span><span class="p">;</span><span class="w"></span>
<span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">height</span><span class="p">;</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">ctx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s1">'2d'</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nx">alpha</span><span class="o">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">});</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="o">!</span><span class="nx">ctx</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'canvas not supported'</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">return</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">globalAlpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">1.0</span><span class="p">;</span><span class="w"></span>
<span class="nx">ctx</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="nx">width</span><span class="p">,</span><span class="w"> </span><span class="nx">height</span><span class="p">);</span><span class="w"></span>
<span class="kd">const</span><span class="w"> </span><span class="nx">pixel_0_0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">getImageData</span><span class="p">(</span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">0</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span><span class="w"> </span><span class="mf">1</span><span class="p">).</span><span class="nx">data</span><span class="p">;</span><span class="w"></span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="nx">pixel_0_0</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">base</span><span class="p">,</span><span class="w"> </span><span class="nx">element</span><span class="p">)</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">base</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="nx">element</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="mf">255</span><span class="p">,</span><span class="w"> </span><span class="kc">false</span><span class="p">))</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'privacy.resistFingerprinting detected'</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="c1">// ...</span><span class="w"></span>
<span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="c1">// normal mode</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h2>Comparison Firefox 64.0 default profile</h2>
<p><img alt="As rendered by Firefox with resistFingerprinting today" src="https://estada.ch/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/images/2018-12-22_firefox-64.0.png"></p>
<h2>Comparison Firefox 64.0 with privacy.resistFingerprinting enabled</h2>
<p><img alt="As rendered by Firefox with resistFingerprinting today" src="https://estada.ch/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/images/2018-12-22_firefox-64.0-resistFingerprinting.png"></p>
<h2>Comparison Chromium 71</h2>
<p><img alt="As rendered by Chromium today" src="https://estada.ch/2018/12/22/firefox-privacy-enhancement-renders-every-off-screen-canvas-white/images/2018-12-22_chromium-71.0.3578.98.png"></p>
<h1>privacy.spoof_english</h1>
<p>Makes <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1447592">JavaScript return</a> this and also restrict the HTTP headers to <code>en-US</code>:</p>
<div class="highlight"><pre><span></span><code><span class="nx">navigator</span><span class="p">.</span><span class="nx">language</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="s2">"en-US"</span><span class="w"></span>
<span class="nx">navigator</span><span class="p">.</span><span class="nx">languages</span><span class="w"> </span><span class="o">===</span><span class="w"> </span><span class="nb">Array</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"en-US"</span><span class="p">,</span><span class="w"> </span><span class="s2">"en"</span><span class="w"> </span><span class="p">]</span><span class="w"></span>
</code></pre></div>
<h1>extensions.ui.locale.hidden</h1>
<p>Hide the language from extensions, I guess since there is <a href="https://getadmx.com/?Category=FrontMotion&Policy=FrontMotion.Policies.Firefox::EXTENSIONS_UI_LOCALE_HIDDEN">no official documentation</a>.</p>
<h1>javascript.use_us_english_locale</h1>
<p>Turns the JavaScript VM into one with the English locale.
This is documented in the bugs <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1369330">1369330</a> and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1039069">1039069</a>.</p>
<h1>app.normandy.user_id</h1>
<p>This setting belongs to <a href="https://wiki.mozilla.org/Firefox/Shield/Heartbeat">Mozillas Heartbeat</a> program.</p>
<h2>Disabling Heartbeat</h2>
<ol>
<li>open <code>about:config</code></li>
<li>toggle app.normandy.enabled to <em>false</em></li>
<li>set app.normandy.user_id to <strong>""</strong></li>
</ol>
<h1>Final words</h1>
<p>Security is not a one-time task, it requires a continued effort.</p>
<p>I wish that mozilla will document these options in the future and gives clarity to its users.</p>
<h2>Update 2018-12-23</h2>
<p>There is <a href="https://support.mozilla.org/de/kb/firefox-protection-against-fingerprinting">some more documentation from mozilla now</a>.</p>
<h2>Browser extensions</h2>
<p>In the beginning I was searching the strange behaviour on my extensions and the operating system specifics.
Cleaning up old extensions was a good thing to do anyways and reducing them to a minimum speeds up the browser.</p>
<p>I am currently recommending:</p>
<ul>
<li>Privacy Badger by EFF Technologists</li>
<li>uBlock Origin by Raymond Hill</li>
</ul>
<p>and please:</p>
<ul>
<li>do <strong>remove</strong> Ghostery because it <a href="https://lifehacker.com/ad-blocking-extension-ghostery-actually-sells-data-to-a-514417864">tried to sell you data</a> or <a href="https://www.heise.de/newsticker/meldung/Ghostery-Erweiterung-blendet-Werbung-ein-4107209.html">straight up adds advertisement</a> even after <a href="https://www.ghostery.com/blog/product-releases/ghostery-goes-open-source/">it became open source</a> .</li>
</ul>
<p>More information is available <a href="https://prism-break.org/en/categories/gnu-linux/#web-browser-addons">eg. here</a>.</p>Rust Embedded Edition 2018 - Rust Zürichsee2018-12-11T00:00:00+01:002018-12-11T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-12-11:/2018/12/11/rust-embedded-edition-2018-rust-zurichsee/<p><a href="https://rnestler.github.io/">Raphael</a> (<a href="https://twitter.com/rnstlr">@rnstlr</a>) gave a great talk about how embedded development works in Rust today. Since <a href="">Rust …</a></p><p><a href="https://rnestler.github.io/">Raphael</a> (<a href="https://twitter.com/rnstlr">@rnstlr</a>) gave a great talk about how embedded development works in Rust today. Since <a href="">Rust 1.31</a> stabilized so much he had to port all of the example code within the last days to <code>stable</code>.</p>
<p>All the <a href="https://github.com/rust-zurichsee/meetups/tree/master/2018-12-11_embedded-update-2018">slides are on our Zürichsee website</a>.</p>
<p>The examples run on a STM32F3DISCOVERY board. To follow the slides make sure you have the following tools available:</p>
<ul>
<li>rustup: <a href="http://rustup.rs">http://rustup.rs</a></li>
<li>latest stable rust toolchain: <code>rustup install stable</code></li>
<li>The Cortex-M4 target: <code>rustup target add thumbv7em-none-eabihf</code></li>
<li>openocd: <a href="http://openocd.org/">http://openocd.org/</a></li>
<li><code>arm-none-eabi-gdb</code></li>
</ul>Rust outreach - an introduction for everyone - Rust Zürichsee Meetup2018-11-15T00:00:00+01:002018-11-15T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-11-15:/2018/11/15/rust-outreach-an-introduction-for-everyone-rust-zurichsee-meetup/<p>Today <a href="https://github.com/gandro">Sebastian</a> talked about the Rust language and it's ecosystem. If you would like to learn …</p><p>Today <a href="https://github.com/gandro">Sebastian</a> talked about the Rust language and it's ecosystem. If you would like to learn more try <a href="https://doc.rust-lang.org/stable/book/">the book</a> or the <a href="https://doc.rust-lang.org/stable/rust-by-example/">Rust by Example</a> series.</p>CAA, site configuration, security and HTTP22018-11-12T00:00:00+01:002018-11-12T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-11-12:/2018/11/12/caa-site-configuration-security-and-http2/<p>Last week I upped the security of this site.
Since it is generated with <a href="https://blog.getpelican.com/" target="_blank">pelican</a> it …</p><p>Last week I upped the security of this site.
Since it is generated with <a href="https://blog.getpelican.com/" target="_blank">pelican</a> it is generated to plain simple static HTML.
So serving the pages relies on DNS, TLS and HTTP.
Therefore I have to build the security from the ground up.</p>
<p><strong>Update 2019-02-28: <a href="/2019/2/28/server-security-update/">Server security update</a></strong></p>
<h1>Step one: DNSSec and CAA</h1>
<p>Everybody viewing the site has to resolve the name <code>estada.ch</code>.
The system used to do that is DNS.
Sadly it is insecure by default.</p>
<p>Lucky us, there is a well tested solution: <a href="https://www.icann.org/resources/pages/dnssec-qaa-2014-01-29-en">DNSSec</a> (<a href="https://www.cloudflare.com/dns/dnssec/how-dnssec-works/">How it works</a>)!
Everyday more and more ISPs use it to verify the records requested by your operating system.</p>
<p>Unfortunately, the browsers do not use it to check certificates.
Certificates accepted by browsers like Firefox still need to be registered with a CA.
As history has shown some CAs <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1311713">made bad mistakes</a>.</p>
<p>To request a certificate everyone needs to go through some sort of validation.
With the free <a href="https://letsencrypt.org/">Let's Encrypt</a> the checks are automated and DNSSec helps to ensure only my systems are able to pass these checks.</p>
<p>However if a CA was not checkin DNSSec it could resolve to a distrusted system if a well organized attacker layed prepared for it.
To prevent that from happening there is a new DNS record type: <a href="https://letsencrypt.org/docs/caa/">CAA</a>.</p>
<p>The CAA records allow an administrator to limit the CAs from which he/she want's to get a certificate and who they should inform if there was an attempt to get a certificate from a unauthorized CA.</p>
<h1>Step two: TLS, HTTP and HTTP2</h1>
<p>After all this preparation time for a bit of eye-candy.
I removed the <code>www</code> with nginx and a permanent redirect.
Another positive benefit is, search engines only index the page once.
This helps with focusing the traffic and simplifies the logging.</p>
<div class="highlight"><pre><span></span><code><span class="k">server</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="mi">443</span><span class="w"> </span><span class="s">ssl</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="s">[::]:443</span><span class="w"> </span><span class="s">ssl</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_certificate</span><span class="w"> </span><span class="s">/etc/letsencrypt/live/estada.ch/fullchain.pem</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_certificate_key</span><span class="w"> </span><span class="s">/etc/letsencrypt/live/estada.ch/privkey.pem</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_timeout</span><span class="w"> </span><span class="s">1d</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_cache</span><span class="w"> </span><span class="s">shared:SSL:10m</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_tickets</span><span class="w"> </span><span class="no">off</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">server_name</span><span class="w"> </span><span class="s">www.estada.ch</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">rewrite</span><span class="w"> </span><span class="s">^(.*)</span><span class="w"> </span><span class="s">https://estada.ch</span><span class="nv">$1</span><span class="w"> </span><span class="s">permanent</span><span class="p">;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<p>On to the long-term improvements.
Enabling <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Evolution_of_HTTP">HTTP/2</a> with <a href="https://nginx.org/">nginx</a> on <a href="https://www.debian.org/">Debian 9</a> is easy, simply add <code>http2</code> to the <code>listen</code> line.
Still the speed benefit is very variable.
Sometimes the pages would load faster, sometimes slower.
In conclusion I will enable it for IPv6 for now and see how it goes.</p>
<p>To tell the browsers to use TLS encryption whenever it tries to connects to <code>estada.ch</code> there is <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security"><strong>HSTS</strong></a>.
I used this on the domain for over a year without any issue.
Today I extended to include all my subdomains and add the domain to the browsers whitelist with <code>includeSubDomains; preload</code>.</p>
<p>The new <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy"><strong>CSP</strong></a> is a huge plus and prevents the site from being embedded with another site via an <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe">iframe</a>: <code>default-src 'self'; img-src 'self' data:; frame-ancestors 'none'; object-src 'none';</code></p>
<h2>New Security Headers</h2>
<p>The <a href="https://scotthelme.co.uk/a-new-security-header-referrer-policy/">Referrer-Policy</a> gives control over the metadata of outgoing requests.
At this time I decided to allow the base referrer on outgoing clicks if the target has HTTPS enabled.</p>
<p>The other is <a href="https://scotthelme.co.uk/a-new-security-header-feature-policy/">Feature-Policy</a> that allows to enable or disable modern features.
Sadly I was unable to find an easy way to disable everything.
So I decided to allow <code>fullscreen</code> and hope it blocks everything else.</p>
<h2>404 catch all</h2>
<p>Finally, every time a document was moved visitors would see a plain 404 error page.
From now on the server will display a <a href="/404.html">templated error page</a>.</p>
<div class="highlight"><pre><span></span><code><span class="k">server</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="mi">443</span><span class="w"> </span><span class="s">ssl</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="s">[::]:443</span><span class="w"> </span><span class="s">ssl</span><span class="w"> </span><span class="s">http2</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">server_name</span><span class="w"> </span><span class="s">estada.ch</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1"># certs sent to the client in SERVER HELLO are concatenated in ssl_certificate</span>
<span class="w"> </span><span class="kn">ssl_certificate</span><span class="w"> </span><span class="s">/etc/letsencrypt/live/estada.ch/fullchain.pem</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_certificate_key</span><span class="w"> </span><span class="s">/etc/letsencrypt/live/estada.ch/privkey.pem</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_timeout</span><span class="w"> </span><span class="s">1d</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_cache</span><span class="w"> </span><span class="s">shared:SSL:10m</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_session_tickets</span><span class="w"> </span><span class="no">off</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1"># modern configuration. tweak to your needs.</span>
<span class="w"> </span><span class="kn">ssl_protocols</span><span class="w"> </span><span class="s">TLSv1.2</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_ciphers</span><span class="w"> </span><span class="s">'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:HIGH:!aNULL:!MD5'</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_prefer_server_ciphers</span><span class="w"> </span><span class="no">on</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1"># HSTS (ngx_http_headers_module is required) (31536000 seconds = 12 months)</span>
<span class="w"> </span><span class="kn">add_header</span><span class="w"> </span><span class="s">Strict-Transport-Security</span><span class="w"> </span><span class="s">"max-age=31536000</span><span class="p">;</span><span class="w"> </span><span class="kn">includeSubDomains</span><span class="p">;</span><span class="w"> </span><span class="kn">preload"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">add_header</span><span class="w"> </span><span class="s">Content-Security-Policy</span><span class="w"> </span><span class="s">"default-src</span><span class="w"> </span><span class="s">'self'</span><span class="p">;</span><span class="w"> </span><span class="kn">img-src</span><span class="w"> </span><span class="s">'self'</span><span class="w"> </span><span class="s">data:</span><span class="p">;</span><span class="w"> </span><span class="kn">frame-ancestors</span><span class="w"> </span><span class="s">'none'</span><span class="p">;</span><span class="w"> </span><span class="kn">object-src</span><span class="w"> </span><span class="s">'none'"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">add_header</span><span class="w"> </span><span class="s">Referrer-Policy</span><span class="w"> </span><span class="s">"origin-when-cross-origin"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">add_header</span><span class="w"> </span><span class="s">Feature-Policy</span><span class="w"> </span><span class="s">"fullscreen</span><span class="w"> </span><span class="s">'self'"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">add_header</span><span class="w"> </span><span class="s">X-Content-Type-Options</span><span class="w"> </span><span class="s">"nosniff"</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1"># OCSP Stapling ---</span>
<span class="w"> </span><span class="c1"># fetch OCSP records from URL in ssl_certificate and cache them</span>
<span class="w"> </span><span class="kn">ssl_stapling</span><span class="w"> </span><span class="no">on</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">ssl_stapling_verify</span><span class="w"> </span><span class="no">on</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="c1">## verify chain of trust of OCSP response using Root CA and Intermediate certs</span>
<span class="w"> </span><span class="kn">ssl_trusted_certificate</span><span class="w"> </span><span class="s">/etc/letsencrypt/live/data.estada.ch/chain.pem</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">root</span><span class="w"> </span><span class="s">/srv/http/estada.ch/htdocs</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kn">location</span><span class="w"> </span><span class="s">/</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="kn">error_page</span><span class="w"> </span><span class="mi">404</span><span class="w"> </span><span class="s">/404.html</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>
<h1>Update 2018-11-13</h1>
<p>I was asked "Why not disable TLS to gain even more speed?"
The simple answer is, there is no real speedup as <a href="https://istlsfastyet.com/">is TLS fast yet</a> shows.</p>
<h1>Update 2019-01-02</h1>
<p>Added <code>add_header X-Content-Type-Options "nosniff";</code> to prevent mistaken content types.</p>
<p>I used the mozilla <a href="https://mozilla.github.io/server-side-tls/ssl-config-generator/">configuration generator</a> for the initial config and secured it from there on.</p>
<p>Reduced <code>ssl_session_cache</code> time from <em>50</em> to <strong>10</strong> minutes.</p>
<h1>Update 2019-02-28</h1>
<p>New post <a href="/2019/2/28/server-security-update/">Server security update</a>.</p>Parallel Programming Workshop at RustFest Rome2018-11-01T00:00:00+01:002018-11-01T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-11-01:/2018/11/1/parallel-programming-workshop-at-rustfest-rome/<p>🎉 My workshop about parallel programming will be at <a href="https://rome.rustfest.eu/">RustFest Rome</a>. 🎉</p>
<p>It will be on Sunday, 2018-11-25 …</p><p>🎉 My workshop about parallel programming will be at <a href="https://rome.rustfest.eu/">RustFest Rome</a>. 🎉</p>
<p>It will be on Sunday, 2018-11-25, follow <a href="https://rome.rustfest.eu/schedule/">the official schedule</a> for more informations.</p>Theater Idee 'Schwache künstliche Intelligenz'2018-10-28T15:15:00+01:002018-10-28T15:15:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-10-28:/2018/10/28/theater-idee-schwache-kunstliche-intelligenz/de/<p>In naher Zukunft ist Big Data die treibende Kraft hinter fast allen wirtschaftlichen Entscheidungen.
In der …</p><p>In naher Zukunft ist Big Data die treibende Kraft hinter fast allen wirtschaftlichen Entscheidungen.
In der Politik nutzen alle die es vermögen Big Data um ihre Ideen durchzusetzen.
Sei es um der Bevölkerung eine unliebsame Entscheidung schmackhaft zu machen oder um die Gegenspieler auflaufen zu lassen.</p>
<p>Bis vor wenigen Jahren waren viele handwerkliche Berufe fast verschwunden, die verbliebenen spezialisierten sich auf Luxusgüter und andere Nischen.
Seit einigen Jahren trafen die riesigen Datenhäufen nun auch die Verwaltungsebene.</p>
<p>In er Privatwirtschaft traf diese Entwicklung das mittlere Management und das Marketing.
Die Schnittstellen zwischen den Abteilungen wurden durch dumme, aber stets höfliche schwache künstliche Intelligenzen ersetzt.
Zwar können diese nicht emotional auf die Mitarbeiter eingehen, da sie aber auch keine Macht- oder Aufstiegswünsche haben kann die Firmenleitung ihre Ziele ungestörter verfolgen.</p>
<p>Zwar gäbe es ein solidarisches Grundeinkommen, allerdings ist das sozial Stigma so gross, dass die meisten lieber bei Verwandten und Freunden unter kommen.</p>
<h1>Handlung</h1>
<h2>1. Akt</h2>
<p>Hauptperson arbeitet bei der Grosskonzern AG.
Das Hauptprodukt der Grosskonzern AG ist eBanking-Software.
Es geht ganz gut, seit immer mehr Verwaltungsaufgaben von Maschinen übernommen werden macht das Arbeiten wieder Spass.
Zwar hört Hauptperson immer Mal wieder, dass die Automatisierung ganze Abteilungen dahin rafft oder den Leuten in den Lagerhäusern den Tag bis auf die letzte Sekunde verplant, doch im direkten Umfeld scheint es allen besser zu gehen.</p>
<p>Eines Tages will Hauptperson gerade Mittagessen gehen, als sein/ihr Teamchef ihn/sie zu sich ruft.
Er werde beurlaubt, sagt er mit einer Träne auf der Wange.
Seine Position wurde durch ein Programm ersetzt und weil das AVP-Programm basierend auf seinen Krankenkassen-Daten, welche inzwischen frei handelbar sind auch wenn der Patient/-in nicht zustimmt, berechnet hatte, dass Teamchef sich in einer niederen Position schlecht auf das Betriebsklima auswirken könnte, muss er die Firma nun verlassen.</p>
<h2>2. Akt</h2>
<p>Die dummen KIs optimieren die Firmen auf geringe wie früher das mittlere Management auf kurzfristige Ziele.
Immer mehr Verwandte müssen sich heimlich um ihre arbeitslosen Nächsten kümmern.
Durch die zusätzliche Belastung können Sie kein Vermögen aufbauen und einige verschulden sich sogar.
Die Prekarisierung schreitet voran.</p>
<p>Als Hauptperson beim Einkaufen bemerkt, dass die Auswahl im Supermarkt auf 15 Produkte reduziert wird beginnt er/sie daran zu zweifeln, dass alles besser wird.</p>
<p>Bei der Arbeit werden den Mitarbeitern inzwischen die Pinkelpausen gestrichen.
Die künstliche Intelligenz AVP hatte beschlossen, dass es effizienter sein wenn die Mitarbeiter ausserhalb der bezahlten Arbeitszeiten urinieren.
Ausserdem würde das gemeinsame Leid den Teamgeist um 20% steigern.</p>
<h2>3. Akt</h2>
<p>Hauptperson hat jetzt einen Entschluss gefasst.
Hauptperson will einen starke künstliche Intelligenz bauen, welche Hauptperson helfen soll sich in der inzwischen mehrheitlich automatisierten Welt zurecht zu finden.</p>
<p>Dass jemand keine produktive Arbeit mehr leistet fällt in der Grosskonzern AG inzwischen nicht mehr auf.
Um Fehler zu verhindern wird jede anfallende Arbeit von zwei Menschen und mindestens von zehn künstlichen Intelligenzen im Trainings-Modus gemacht.
Hauptperson weiss damit, dass es nur noch eine Frage der Zeit ist bis ihm/ihr seine/ihre Aufgabe und vielleicht sogar der Arbeitsplatz weggenommen wird.</p>
<h2>Pause</h2>
<h2>4. Akt</h2>
<p>Der Beamte schreitet durch die Hallen.
Seit er diesen Beruf mit 25 nach dem Studium begonnen hat verhält er sich gleich.</p>
<ol>
<li>Früh Morgens ankommen.</li>
<li>Formulare und Briefe von einem Stapel in einem Büro holen.</li>
<li>Sobald alles bei seinem Schreibtisch ist, kontrollieren ob auf dem Papier das gleiche steht wie im System eingetragen ist.</li>
<li>Am Abend, die erledigen Papiere in ein drittes Büro bringen und die unerledigten zurück ins erste.</li>
</ol>
<p>Doch heute ist etwas anders.
Heute ist jemand fremdes tritt gerade in sein Büro.
"Hauptperson, der Name" sagt die neue Person freundlich.</p>
<p>Als der Beamte nicht reagiert fragt Hauptperson warum sich in der Administration in den letzten 20 Jahren nichts verändert hatte während sich die Welt draussen komplett gewandelt hatte.
Die Antwort würde Hauptperson nicht gefallen verteidigte sich der Beamte, schliesslich müssen die Gesetzte eingehalten werden.</p>
<p>Die Gesetzte also.
Der letzte Gral, welcher durch das Grundgesetz vor den künstlichen Intelligenzen geschützt wird.
Die Elite hatte sich vor einem Machtverlust gefürchtet und ihn darum einfach gesetzlich verboten.</p>
<h2>5. Akt</h2>
<p>Hauptperson will sich absichern und hat Angst vor der Scham des sozialen Grundeinkommens.
Was würden die Freunde und Verwandten denken?</p>
<p>Ein neuer Plan muss her.
Die Geschicke der Welt zu beeinflussen ist zwar möglich, braucht aber extremes Fachwissen und Unterstützung von anderen.
Mit all den persönlichen künstlichen Intelligenzen in der Welt, würden auch kleine Fehler jedes Vorhaben zunichte machen.
Eine künstliche Intelligenz zu schaffen, welche die Gesetzte nicht nur ignoriert sonder sie auch zu verändern sucht.</p>
<div class="highlight"><pre><span></span><code>Die Übernahme der starken KI.
Tod.
</code></pre></div>
<h1>Rollen:</h1>
<ul>
<li>Hauptperson</li>
<li>Beamte</li>
<li>Politiker/-innen</li>
<li>Verkäufer</li>
<li>Arbeitsloser Manager (Teamchef)</li>
<li>Arbeitsklima-Verbesserungs-Programm AVP (evt. nur Stimme und Licht)</li>
</ul>Atomics and visibility problem2018-10-24T00:00:00+02:002018-10-24T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-10-24:/2018/10/24/atomics-and-visibility-problem/<p>In this talk I will go into details of the visibility problem inside one CPU and …</p><p>In this talk I will go into details of the visibility problem inside one CPU and how the manufacturers trade speed and vs. reliability.</p>
<p><a href="https://estada.ch/2018/10/24/atomics-and-visibility-problem/slides/2018-10-24_rust-zuerichsee-at-coredump.pdf"><img alt="Download Slides" src="https://estada.ch/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>Building exchange on Exonum Workshop - Rust Zürichsee2018-09-18T00:00:00+02:002018-09-18T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-09-18:/2018/9/18/building-exchange-on-exonum-workshop-rust-zurichsee/<p>This month will be a surprise. Ilya Bogdanov, Rust Developer at Exonum, Bitfury Group is visiting …</p><p>This month will be a surprise. Ilya Bogdanov, Rust Developer at Exonum, Bitfury Group is visiting us with the subject "Building exchange on Exonum in Rust".</p>
<p>We will talk about using Rust in real-world applications and Exonum framework and its internal structure. We will learn how to create blockchain-based applications with Rust and Exonum. Practical part includes creating ready-to-use blockchain exchange with both backend and frontent parts written in Rust and then deployment of a scalable blockchain network on several nodes.</p>
<p>Please bring:
* Your laptop
* With the installed rust toolchain, Installer is available here: <a href="https://rustup.rs/">https://rustup.rs/</a>
* Configured developer environment is not necessary - one can use GitHub repository with source code and prepared binaries.
* Some preparation is need for ones who want to actively participate in workshop - follow Exonum installation guide to set up necessary dependencies. (<a href="https://exonum.com/doc/get-started/install/">https://exonum.com/doc/get-started/install/</a>) You don't need to compile Exonum itself.</p>
<p>Schedule</p>
<ul>
<li>Doors open at 18:30</li>
<li>Introduciton Talk starts around 19:00</li>
<li>After that the Workshop commences</li>
<li>After you may stay and talk to people for a while.</li>
</ul>
<p>If we hit the capacity limit, please message us.</p>Actix.rs - Rust Zürichsee2018-07-11T00:00:00+02:002018-07-11T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-07-11:/2018/7/11/actixrs-rust-zurichsee/<p><a href="https://twitter.com/implgeo">Pirmin Kalberer</a> (<a href="https://github.com/pka">@pka</a>) will talk about <a href="https://actix.rs/">actix.rs</a>, a new asynchronous and blazing fast Rust web …</p><p><a href="https://twitter.com/implgeo">Pirmin Kalberer</a> (<a href="https://github.com/pka">@pka</a>) will talk about <a href="https://actix.rs/">actix.rs</a>, a new asynchronous and blazing fast Rust web framework. It supports WebSockets, HTTP/2, pipelining out of the box and is based on an actor framework. This talk gives an introduction into actix and shows it in practical use in three web applications.</p>
<p>This time we are hosted by Liip. Thank you!</p>
<ul>
<li>Doors will open around 18:30</li>
<li>Talk will start at 19:00</li>
<li>After the talk everybody is invited to stay, talk, hack or enjoy the summer.</li>
</ul>
<p>You have a cool project, strange problem or an interesting topic you wanted to present for a long time?
Look no more, write me :)</p>Iteratoren und Stream Processing mit Rust - Cosin 20182018-06-17T00:00:00+02:002018-06-17T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-06-17:/2018/6/17/iteratoren-und-stream-processing-mit-rust-cosin-2018/de/<p>Die Aufzeichung von der Cosin 2018 ist online:
<a href="https://media.ccc.de/v/cosin2018-99-iteratoren_und_stream_processing_mit_rust">Iteratoren und Stream Processing mit Rust - freundliche Parallele …</a></p><p>Die Aufzeichung von der Cosin 2018 ist online:
<a href="https://media.ccc.de/v/cosin2018-99-iteratoren_und_stream_processing_mit_rust">Iteratoren und Stream Processing mit Rust - freundliche Parallele Programme mit Rust</a></p>June Community Meetup - Rust Zürichsee2018-06-12T00:00:00+02:002018-06-12T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-06-12:/2018/6/12/june-community-meetup-rust-zurichsee/<p>This month we will enjoy happy hacking at <a href="https://www.coredump.ch/">coredump</a>.
Bring your laptop and your friends.</p>Iterators - Rust Zürichsee2018-05-08T00:00:00+02:002018-05-08T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-05-08:/2018/5/8/iterators-rust-zurichsee/<p>I talked at the <a href="https://www.tik.ee.ethz.ch/">TIK institute of ETH</a> about Iterators and what you can do with …</p><p>I talked at the <a href="https://www.tik.ee.ethz.ch/">TIK institute of ETH</a> about Iterators and what you can do with them when developing parallel programs.</p>
<p><a href="/slides/2018-05-04-iterators-and-threadpools-htwg-fh-konstanz.pdf"><img alt="Download Slides" src="/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>Thread pools and iterators2018-05-04T00:00:00+02:002018-05-04T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-05-04:/2018/5/4/thread-pools-and-iterators/<p>I held this talk infront of bachelor students at HTWG Konstanz.</p>
<p><a href="/slides/2018-05-04-iterators-and-threadpools-htwg-fh-konstanz.pdf"><img alt="Download Slides" src="/images/breeze_viewpdf.svg"><br>Slides.pdf</a></p>Embedded Rust Meetup - Rust Zürichsee2018-01-30T00:00:00+01:002018-01-30T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2018-01-30:/2018/1/30/embedded-rust-meetup-rust-zurichsee/<h2>What to expect:</h2>
<p>Raphael will talk about Rust on embedded systems.</p>
<h2>Abstract:</h2>
<p>I will show the …</p><h2>What to expect:</h2>
<p>Raphael will talk about Rust on embedded systems.</p>
<h2>Abstract:</h2>
<p>I will show the current state of embedded in Rust by shortly introducing notable projects like tockos, xargo and svd2rust. We will then look at a concrete project I worked on which involves an LPC11U24 ARM micro controller and the steps which are needed until Rust code runs on it!</p>
<p>After the talk, there will be time to eat, hack and mingle. Therefore feel free to bring your laptop and an empty stomach.</p>
<h2>Remark:</h2>
<p>Please keep in mind that the doors of the building are locked after 19:00. Please let us know if you arrive late (here or on Twitter), so we can let you in.</p>Coaching at Django Girls Rapperswil2017-02-18T00:00:00+01:002017-02-18T00:00:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2017-02-18:/2017/2/18/coaching-at-django-girls-rapperswil/<p>The after the <a href="https://www.python-summit.ch/">Swiss Python Summit 2017</a> I coached four women at <a href="https://djangogirls.org/rapperswil/">Django Girls Rapperswil</a>.</p>Mind Tux Map Manual2015-01-18T21:08:00+01:002015-01-18T21:08:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2015-01-18:/2015/1/18/mind-tux-map/<p>The english manual of Mind Tux Map</p><h1>Intro</h1>
<p>Welcome to the MindTuxMap-Documentaton-Project.</p>
<h1>Get the App</h1>
<p>You can get the Editor App from the <a href="https://play.google.com/store/apps/details?id=ch.estada.mindtuxmap" target="_blank"><img src="https://estada.ch/mindtuxmap/img/get_it_on_play_logo_small.png" alt="Play Store" /></a></p>
<p>For starters, you can get the free Viewer App from the <a href="https://play.google.com/store/apps/details?id=ch.estada.mindtuxmap.viewer" target="_blank"><img src="https://estada.ch/mindtuxmap/img/get_it_on_play_logo_small.png" alt="Play Store" /></a></p>
<h1>Basics</h1>
<p>MindTuxMap was built for your fingers. Most gestures are for one finger.<br>
<img src="https://estada.ch/mindtuxmap/img/basic_tutorial_en.png" class="zoomable gross" /></p>
<h2>Creating new Elements</h2>
<p>Swipe your finger from an existing Element away.</p>
<h2>Edit Elements</h2>
<p>Tap on an existing Element.</p>
<h2>Move Elements</h2>
<p>Put your finger on an Element and wait until you see blue arrows around the Element.
The arrows will appear after half a second. Then move your finger without lifting it up and the Element will follow it.</p>
<h2>Deleting Elements</h2>
<p>Tap on an existing Element, choose the "Options"-Button, then "Delete", confirm the dialog.</p>
<h2>Pick Color</h2>
<p>Tap on an existing Element, choose the "Options"-Button, then "Pick Color", finally click at the color you would like to select.</p>
<p>Consider the flag on the bottom. If activated it will change the color of the childrens element.</p>
<h2>Rename the Mindmap</h2>
<p>Tap the root Element of the map or choose the menu entry from the options menu.</p>
<h2>Scale the map</h2>
<p>Put two fingers on the map, but not on an Element. Then move them towards to zoom away.
Move them apart to zoom in.</p>
<p>The map moves when you move your fingers.</p>
<h1>Settings</h1>
<p>The settings offer you control over some details:</p>
<ul>
<li>Export colors to FreeMind</li>
<li>Autoexport the Mindmaps to SD-Card</li>
<li>Export transparent PNG files</li>
</ul>
<p>You can open the settings with the menu at the start screen.</p>
<h1>How to import Mindmaps</h1>
<p>The easiest way to import a MindMap, from FreeMind or MindTuxMap, is to open/display it with the (g-)MailApp or an ExplorerApp
(like
<a href="https://play.google.com/store/apps/details?id=xcxin.filexpert" target="_blank">File Expert</a>,
<a href="https://play.google.com/store/apps/details?id=com.rhmsoft.fm" target="_blank">File Manager</a> or any other FileBrowser).</p>
<p>If you use <a href="https://play.google.com/store/apps/details?id=com.rhmsoft.fm" target="_blank">File Manager</a>, it works like this:</p>
<ul>
<li>Enter the Directory containing your File and tap it.<br>
<img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_1.png"></li>
<li>Select "Open as text"<br>
<img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_2.png"></li>
<li>Select "MindTuxMap Import" and click on "just once"<br>
<img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_3.png"></li>
</ul>
<h2>Supported file types</h2>
<p>MindTuxMap currently supports:</p>
<ul>
<li>MindTuxMap, files end with .mtm</li>
<li><a href="http://freemind.sourceforge.net/" target="_blank">FreeMind</a> v0.9, files end with .mm</li>
</ul>
<h1>Share your work</h1>
<p>You can export every Mindmap you created or imported via the menu at the start screen.</p>
<p>If you opened your map, you can export it directly and attach it to an email if you like.</p>
<h2>Export formats</h2>
<p>MindTuxMap currently supports:
* MindTuxMap</li>
* <a href="http://freemind.sourceforge.net/" target="_blank">FreeMind</a> v0.9</li>
* PNG (transparent background, see <a href="#settings">settings</a>)</li>
* JPG</li></p>
<h1>Use a Cloudservice</h1>
<p>If you would like to sync your Data with an cloud service (like Dropbox or others), you will have to export your Mindmap
from MindTuxMap after you changed it on the device. And you will have to import it everytime you changed it on any other device.</p>
<p>This problem is by design: MindTuxMap was not designed to support cloud storage.</p>
<p>If you need this particular feature: please <a href="#donations">donate</a></p>
<h1>Donate for ...</h1>
<p>If you like this app, would like tu support the development or need a special feature, please consider this:</p>
<p>Write me an email to <a href="mailto:mindtuxmap@estada.ch">mindtuxmap@estada.ch</a> with your suggestion and
donate an appropriate amount.</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="73BKGL45FSHLJ">
<input type="image" src="https://estada.ch/mindtuxmap/img/btn_donate_LG.gif"
border="0" name="submit" alt="Donate with PayPal.">
</form>
<p>Thank you!</p>Mind Tux Map Handbuch2015-01-18T21:08:00+01:002015-01-18T21:08:00+01:00Stefan Schindler (@dns2utf8)tag:estada.ch,2015-01-18:/2015/1/18/mind-tux-map/de/<p>Das deutsche Handbuch zu Mind Tux Map</p><h1>Einleitung</h1>
<p>Willkommen beim MindTuxMap-Dokumentations-Projekt.</p>
<h1>App erhalten</h1>
<p>Die MindTuxMap App ist auf <a href="https://play.google.com/store/apps/details?id=ch.estada.mindtuxmap" target="_blank"><img src="https://estada.ch/mindtuxmap/img/get_it_on_play_logo_small.png" alt="Play Store"></a> erhältlich.
Möchten sie Mindmaps nur betrachten, können Sie MindTuxMap Viewer gratis von <a href="https://play.google.com/store/apps/details?id=ch.estada.mindtuxmap.viewer" target="_blank"><img src="https://estada.ch/mindtuxmap/img/get_it_on_play_logo_small.png" alt="Play Store"></a> herunterladen.</p>
<h1>Grundlagen</h1>
<p>MindTuxMap wurde für Ihre Finger entwickelt. Die Eingaben werden mit einem oder zwei Finger getätigt.<br>
<img src="https://estada.ch/mindtuxmap/img/basic_tutorial_en.png" class="zoomable gross"></p>
<h2>Neue Elemente erstellen</h2>
<p>Fahren Sie mit Ihrem Finger von einem bestehenden Element weg. Schon entsteht ein neues Element.</p>
<h2>Elemente bearbeiten</h2>
<p>Drücken Sie auf ein bestehendes Element.</p>
<h2>Elemente verschieben</h2>
<p>Drücken Sie lange auf ein Element und warten Sie bis blaue Pfeile um das Element sichtbar werden.
Spätestens nach einer Halben Sekunde können Sie das Element verschieben. Dann verschieben Sie Ihren Finger ohne ihn anzuheben und das Element wird ihm folgen.</p>
<h2>Elemente löschen</h2>
<p>Drücken Sie auf ein bestehendes Element, drücken Sie auf den Knopf "Optionen", dann "Ast löschen" und bestätigen Sie den Dialog.</p>
<h2>Elemente einfärben</h2>
<p>Drücken Sie auf ein bestehendes Element, drücken Sie auf den Knopf "Optionen", dann "Farbe wählen". Wählen Sie aus der Liste die
gewünschte Farbe aus.</p>
<p>Bitte beachten Sie das Häkchen am Ende der Liste. Wenn es aktiv ist, werden die Unterelemente auch eingefärbt.</p>
<h2>Mindmap umbenennen</h2>
<p>Drücken Sie entweder auf das graue Wurzelelement oder wählen Sie im Menü den Eintrag "Umbenennen".</p>
<h2>Karte skalieren/zoomen</h2>
<p>Legen Sie zwei Finger auf die Karte:<br>
Schieben Sie Ihre Finger nun zusammen um die Karte zu verkleinern.<br>
Schieben Sie Ihre Finger auseinander um die Karte zu vergrössern.</p>
<p><b>Wichtig:</b> Sie müssen Ihre Finger auf freien Bereichen platzieren.
Die Karte kann sich während diesem Vorgang bewegen.</p>
<h1>Einstellungen</h1>
<p>Die Einstellungen erlauben Ihnen folgende Details zu kontrollieren:</p>
<ul>
<li>
<p>Farben nach FreeMind exportieren: <br>
Wenn diese Option aktiviert ist, werden die Hintergrundfarben der Elemente mit exportiert.</p>
</li>
<li>
<p>Autoexport der Mindmaps auf die SD-Karte: <br>
Das Aktivieren dieser Option exportiert alle Mindmaps im MindTuxMap-Format auf die SD-Karte, welche Sie öffnen oder bearbeiten.
Bitte beachten Sie, dass das Importieren der Mindmaps zur Zeit nicht automatisiert ist.</p>
</li>
<li>
<p>Exportiere transparente PNG-Dateien: <br>
Mit dieser Option können Sie Ihre Mindmaps ohne schwarzen Hintergrund exportieren.
Bitte beachten Sie, dass dies etwas mehr Speicherplatz und viel mehr RAM benötigt.
Sollte das Exportieren mit dieser Option fehlschlagen, versuchen Sie zuerst diese Option zu deaktivieren.</p>
</li>
</ul>
<p>Sie können die Einstellungen auf dem Startschirm mit dem Menü öffnen.</p>
<h1>Mindmaps importieren</h1>
<p>Wenn Sie ein Mindmap importieren möchten geht das am einfachsten, wenn Sie es Öffnen bzw. Anzeigen.
Viele Applikationen wie gMail oder DateiManager (wie zum Beispiel
<a href="https://play.google.com/store/apps/details?id=xcxin.filexpert" target="_blank">File Expert</a>,
<a href="https://play.google.com/store/apps/details?id=com.rhmsoft.fm" target="_blank">File Manager</a> oder andere DateiVerwalter)
bieten dafür einen Knopf.</p>
<p>Wenn Sie <a href="https://play.google.com/store/apps/details?id=com.rhmsoft.fm" target="_blank">Dateimanager</a> verwenden,
funktioniert es so:</p>
<ul>
<li>Wechseln Sie in das Verzeichnis, welches Ihre Mindmap-Datei enthält und klicken Sie die Datei an.<br><img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_1.png"></li></li>
<li>Wählen Sie "Öffnen als Text"<br><img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_2.png"></li></li>
<li>Wählen Sie "MindTuxMap Import" und klicken Sie auf "Nur einmal"<br><img class="zoomable" src="https://estada.ch/mindtuxmap/img/import_filemanager_3.png"></li></li>
</ul>
<h2>Unterstützte Dateiformate</h2>
<p>MindTuxMap unterstützt im Moment:</p>
<ul>
<li>MindTuxMap, Dateien enden auf .mtm</li></li>
<li><a href="http://freemind.sourceforge.net/" target="_blank">FreeMind</a> v0.9, Dateien enden auf .mm</li></li>
</ul>
<h1>Teilen Sie Ihre Arbeit</h1>
<p>Sie können alle Ihre Mindmaps aus dem Menu des Startschirm exportieren.</p>
<p>Wenn Sie ein Mindmap geöffnet haben, können Sie es über das Menu direkt exportieren und wenn Sie wollen an ein E-Mail anhängen.</p>
<h2>Exportformate</h2>
<p>MindTuxMap unterstützt im Moment folgende Formate:</p>
<ul>
<li>MindTuxMap</li>
<li><a href="http://freemind.sourceforge.net/" target="_blank">FreeMind</a> v0.9</li>
<li>PNG (transparenter Hintergrund, siehe <a href="#settings">Einstellungen</a>)</li>
<li>JPG</li>
</ul>
<h1>Cloud Dienste verwenden</h1>
<p>Wenn Sie Ihre Mindmaps mit einem Cloud Dienst (wie z.B. Dropbox oder ähnliche) synchronisieren möchten,
müssen Sie entweder die Auto-Export-Funktion in den <a href="#settings">Einstellungen</a> aktivieren oder
die jeweiligen <a href="#export">Mindmaps exportieren</a> nachdem Sie Ihre Änderungen daran auf dem mobilen Gerät vorgenommen haben.</p>
<p>Zudem müssen Sie die entsprechenden Mindmaps jedesmal wieder importieren, wenn Sie Ihre Mindmap auf einem Ihrer anderen Geräte verändert haben.</p>
<h1>Spenden für ...</h1>
<p>Wenn Sie die Applikation mögen, ihre Entwicklung fördern möchten oder eine bestimmte Funktionalität benötigen, ziehen Sie dies in Betracht:</p>
<p>Schreiben Sie mir ein E-Mail an <a href="mailto:mindtuxmap@estada.ch">mindtuxmap@estada.ch</a> mit Ihrem Vorschlag und
spenden Sie einen angemessenen Betrag.</p>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="73BKGL45FSHLJ">
<input type="image" src="https://estada.ch/mindtuxmap/img/btn_donate_LG.gif"
border="0" name="submit" alt="Donate with PayPal.">
</form>
<p>Vielen Dank!</p>Estada.ch enters the world 🌐2011-06-29T00:00:00+02:002011-06-29T00:00:00+02:00Stefan Schindler (@dns2utf8)tag:estada.ch,2011-06-29:/2011/6/29/estadach-enters-the-world/<p>Hello, 🌐!</p>
<p>Site under 🚧</p>
<p>📨 will be ready soon.</p>