<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Heiko Carrasco</title><link>https://miterion.de/</link><atom:link href="https://miterion.de/index.xml" rel="self" type="application/rss+xml"/><description>Heiko Carrasco</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Wed, 23 Feb 2022 19:08:04 -0500</lastBuildDate><image><url>https://miterion.de/media/icon_hu_fd4805ff676fd9da.png</url><title>Heiko Carrasco</title><link>https://miterion.de/</link></image><item><title>Adaptive Sampling using Stein's Discrepancy</title><link>https://miterion.de/project/adaptive_baselines/</link><pubDate>Wed, 23 Feb 2022 19:08:04 -0500</pubDate><guid>https://miterion.de/project/adaptive_baselines/</guid><description>&lt;h2 id="project-description"&gt;Project Description&lt;/h2&gt;
&lt;p&gt;Investigation into the usage of different sampling methods based on Stein&amp;rsquo;s discrepancy in the context
of Curriculum Learning.&lt;/p&gt;
&lt;p&gt;Implemented both a gradient-based approach (Stein&amp;rsquo;s Variational Gradient Descent&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;) and a direct minimization
method (Stein Points&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;) in &lt;a href="https://github.com/google/jax" target="_blank" rel="noopener"&gt;jax&lt;/a&gt; and evaluated them on different RL baselines.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Liu et al., &lt;a href="https://proceedings.neurips.cc/paper/2016/hash/b3ba8f1bee1238a2f37603d90b58898d-Abstract.html" target="_blank" rel="noopener"&gt;Stein Variational Gradient Descent: A General Purpose Bayesian Inference Algorithm&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Chen et al., &lt;a href="https://proceedings.mlr.press/v80/chen18f.html" target="_blank" rel="noopener"&gt;Stein Points&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Debugging SAC Implementations in ten Pull Requests (or less)</title><link>https://miterion.de/post/sacsimulacra/</link><pubDate>Sun, 23 Jan 2022 14:11:46 -0500</pubDate><guid>https://miterion.de/post/sacsimulacra/</guid><description>&lt;p&gt;During the last semester I was working on a research project, where we combined Curriculum Learning with Domain Randomization to increase Sim-To-Real performance without lengthy data acquisition involving the targeted platform.
Part of this work involved the open-source Reinforcement Learning library &lt;a href="https://github.com/famura/SimuRLacra" target="_blank" rel="noopener"&gt;SimuRLacra&lt;/a&gt;, which was written by my supervisor and provided us with the necessary algorithms and environments on which we could implement and test our idea.&lt;/p&gt;
&lt;p&gt;Unfortunately, the provided Soft Actor Critic (SAC) implementation was not working as well as it should (meaning not at all &amp;#x1f605;). Debugging this led me down an interesting but also frustrating path, which I want to recount here together with their appropriate pull requests.
So get ready for a deep dive into one of the weirdest (and longest) debugging sessions I ever had.&lt;/p&gt;
&lt;h2 id="step-1-kinda-what-magic-numbers-should-i-use"&gt;Step 1 (kinda): What Magic Numbers should I use?&lt;/h2&gt;
&lt;p&gt;Why only kinda? Well, the usual way to debug a problem in anything related to machine learning is to fiddle with the hyperparameters of the model until you see &lt;em&gt;something&lt;/em&gt;.
If you have ever worked with SAC you probably know this table, which shows a certain choice of them made by the creator of the algorithm himself&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;figure id="figure-some-potential-hyperparameters-for-sac"&gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img alt="Some potential Hyperparameters for SAC" srcset="
/post/sacsimulacra/sac-hpos_hu_9400333834b0a30f.webp 400w,
/post/sacsimulacra/sac-hpos_hu_5fd456a7eef6bf65.webp 760w,
/post/sacsimulacra/sac-hpos_hu_56fa8787f1b2c7b7.webp 1200w"
src="https://miterion.de/post/sacsimulacra/sac-hpos_hu_9400333834b0a30f.webp"
width="760"
height="410"
loading="lazy" data-zoomable class=" img-light" /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered"&gt;
Some potential Hyperparameters for SAC
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;While they form a good starting point, they are also not optimal to every application and especially not to every implementation of SAC.
So usually it requires a lot of guesswork to find good hyperparameters to your problem (except when you are one of FANG, where computing time is free&amp;#x2122;&amp;#xfe0f;).
I don&amp;rsquo;t think that this process is particularly interesting and since it also did not work I am skipping it and move on to the next step in the process:&lt;/p&gt;
&lt;h2 id="step-2-is-it-you-or-is-it-me---finding-a-baseline"&gt;Step 2: Is it you or is it me? - Finding a baseline&lt;/h2&gt;
&lt;p&gt;After trying this approach for a long time together with my advisor we decided that it is probably not the correct choice of the &amp;ldquo;magic numbers&amp;rdquo; but something else in the implementation or the environment.
Since we had algorithms able to solve the environment (e.g. a working PPO implementation), it was probably the former of the two.
Following this logic it seemed a good idea to test whether other implementations of SAC would perform similarly on the same task and environment.&lt;/p&gt;
&lt;figure id="figure-the-quanser-qube-platform-which-was-used-as-the-target-environment"&gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img alt="The Quanser Qube platform which was used as the target environment" srcset="
/post/sacsimulacra/quanser_hu_23716ff7905f26dc.webp 400w,
/post/sacsimulacra/quanser_hu_1ae6226e638448.webp 760w,
/post/sacsimulacra/quanser_hu_f0946f0a25d15073.webp 1200w"
src="https://miterion.de/post/sacsimulacra/quanser_hu_23716ff7905f26dc.webp"
width="234"
height="393"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption data-pre="Figure&amp;nbsp;" data-post=":&amp;nbsp;" class="numbered"&gt;
The Quanser Qube platform which was used as the target environment
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Some of the best implementations of a lot of common algorithms are provided by the &lt;a href="https://github.com/DLR-RM/stable-baselines3" target="_blank" rel="noopener"&gt;stable-baselines&lt;/a&gt; project.
Unfortunately, they require a specific interface to the environment such that their implementation can communicate and control without having to be adapted to every new environment.
While this interface, the &lt;a href="https://gym.openai.com/" target="_blank" rel="noopener"&gt;openai gym&lt;/a&gt; is fairly standardized, it still required some work to adapt to SimuRLacra&amp;rsquo;s specific implementation of the Quanser Qube&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So my first pull requests was the creation of an interface between SimuRLacra and gym.
&lt;a href="https://github.com/famura/SimuRLacra/pull/81" target="_blank" rel="noopener"&gt;
&lt;figure &gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img src="https://opengraph.githubassets.com/1230876af7ada1e8d106f5f7cf23e25d04959b8fde72204ae32c5e95d9aa06b7/famura/SimuRLacra/pull/81" alt="The first Pull Request" loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After everything in place I was now able to use the implementation of SAC provided by stable-baselines with our environment and see how well it performs.&lt;/p&gt;
&lt;h2 id="step-3-initialization-is-key-ming"&gt;Step 3: Initialization is Key(-ming)&lt;/h2&gt;
&lt;p&gt;Running a few experiments with the stable-baselines implementation turned out to be eye-opening.
Not only did it run much faster than the implementation provided by SimuRLacra, it also actually solved the task instead of stopping at a sub-optimal solution.
To add further insult to injury it did all this without any changes to the default hyperparameters, which made the previous weeks look like a lot of wasted time on my end &amp;#x1f622;.
Looking at these results I decided to investigate further, trying to find out why there is such a mismatch between these two implementation.&lt;/p&gt;
&lt;p&gt;One of the first avenues I took in debugging this problem were the neural network architectures of both implementations.
SAC uses two neural networks called &lt;strong&gt;actor&lt;/strong&gt; and &lt;strong&gt;critic&lt;/strong&gt;, which are trained together.
Their roles can be inferred from their names: While the actor proposes an action and the critic judges it and provides the feedback to the actor.
Both are usually trained together &lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; and there are a lot of interdependencies between them, which can be encoded into the architecture of the underlying networks&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;In my case I was looking into such a structural difference to explain the initial performance gap: Without any training, the stable-baselines implementation outperformed the one provided by SimuRLacra significantly.
As it turned out however, both implementation did not differ that much in their architectural choices, those being size and activation functions.
It took me some time and looking at a lot of gradients to find one major difference: &lt;strong&gt;Initialization&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;From my perspective there is not that much talk about neural network initialization in the supervised and unsupervised space&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;, but even less in the area of reinforcement learning.
This is quite curious, since a good initialization can make or break a good experiment run due to its influence on the initial exploration of the agent.
However, many RL frameworks just defer to the standard initialization provided by the underlying library like pytorch or tensorflow and do not make any explicit changes to it.
This was not the case for SimuRLacra however.&lt;/p&gt;
&lt;p&gt;While SimuRLacra usually follows the conventions of pytorch, it did differ significantly in the case of initialization.
Instead of just using the defaults, initialization was done explicitly, inspired by the &amp;ldquo;current&amp;rdquo; implementation of pytorch.
But this code was not updated in some time and while pytorch moved on to use the &lt;em&gt;Kaiming initialization&lt;/em&gt; &lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;, SimuRLacra just did not.
I therefore adapted the current code to follow pytorchs path, leading to the second pull request (which technically came after the first one, but that is just the order I opened them in, not the order they were created).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/famura/SimuRLacra/pull/72" target="_blank" rel="noopener"&gt;
&lt;figure &gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img src="https://opengraph.githubassets.com/7067755b339a767c614e6c95b2d99f440b03ee6cc7b2a552a1fe9914d8965fff/famura/SimuRLacra/pull/72" alt="The second Pull Request" loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And that had a huge impact!
Not only in the case of SAC, but also for other algorithms like PPO the performance increased significantly.
&lt;div class="gallery-grid"&gt;
&lt;div class="gallery-item gallery-item--medium"&gt;
&lt;a data-fancybox="gallery-sacsimulacra" href="https://miterion.de/media/albums/sacsimulacra/0-old-rnn.png" data-caption="RNN perfomance with the old initialization"&gt;
&lt;img src="https://miterion.de/media/albums/sacsimulacra/0-old-rnn_hu_79b2b398e472d641.webp" loading="lazy" alt="RNN perfomance with the old initialization" width="600" height="294"&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;div class="gallery-item gallery-item--medium"&gt;
&lt;a data-fancybox="gallery-sacsimulacra" href="https://miterion.de/media/albums/sacsimulacra/1-new-rnn.png" data-caption="RNN perfomance with Kaiming"&gt;
&lt;img src="https://miterion.de/media/albums/sacsimulacra/1-new-rnn_hu_3f8eab50f94f2f17.webp" loading="lazy" alt="RNN perfomance with Kaiming" width="600" height="286"&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;strong&gt;Old vs new performance&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While this moved the SimuRLacra implementation closer to the stable-baselines one, it did not bring it up to par in regards of later performance: Still the task was not being solved, but at least the intermediate performance looked promising.&lt;/p&gt;
&lt;h2 id="step-4-what-to-remember"&gt;Step 4: What to remember?&lt;/h2&gt;
&lt;p&gt;I want to write a blog post about the underlying project (Self-Paced Domain Randomization) at some point, so I won&amp;rsquo;t get into too much detail here regarding the intricacies of this specific application.
Do understand the next step, however, I will give a short intro to Curriculum Learning.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;To be continued&lt;/em&gt;&lt;/p&gt;
&lt;!--### Intermission Curriculum Learning
If we want to learn a new skill, say shooting a basketball through a hoop, we usually do not start with the most difficult version of the skill first.
In the case of basketball, many people will start throwing the ball while standing quite close to the hoop instead of
--&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Haarnoja et al., &lt;a href="https://arxiv.org/abs/1812.05905" target="_blank" rel="noopener"&gt;Soft Actor-Critic Algorithms and Applications&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The gym interface is also not really documented and the missing type annotations make the implementation a little bit of guesswork and trial and error.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;But not directly updated together in the case of SAC&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;For example you can have actor and critic share the same (learned) feature transformation&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;That does not mean that initialization is a widely disregarded topic; far from it. I just want to point out that it is often glossed over in RL literature.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;He et al., &lt;a href="https://openaccess.thecvf.com/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html" target="_blank" rel="noopener"&gt;Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification&lt;/a&gt;&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description></item><item><title>Self-Paced Domain Randomization</title><link>https://miterion.de/project/spdr/</link><pubDate>Wed, 01 Sep 2021 00:00:00 +0000</pubDate><guid>https://miterion.de/project/spdr/</guid><description>&lt;h2 id="project-description"&gt;Project Description&lt;/h2&gt;
&lt;p&gt;Implemented a novel approach to Domain Randomization based on Curriculum Learning to bridge the &lt;em&gt;Sim-2-Real&lt;/em&gt; gap.
Evaluated on a real robotic platform (Quanser Qube).
The final code was upstreamed to an open-source reinforcement learning library.&lt;/p&gt;
&lt;!--
#Training Reinforcement Learning agents directly on a real platform is often not feasible due to time and cost constraints.
#While simulators can help with this problem, they are often imprecise and do not model the real world accurately (enough).
#This so-called *reality gap* can create problems when a simulator-trained policy is tested on a real platform.
#
#While there are some ideas like ADR
--&gt;</description></item><item><title>ldap2carddav</title><link>https://miterion.de/project/ldap2carddav/</link><pubDate>Mon, 05 Oct 2020 21:35:42 -0500</pubDate><guid>https://miterion.de/project/ldap2carddav/</guid><description/></item><item><title>D120 Web Projects</title><link>https://miterion.de/project/django/</link><pubDate>Wed, 01 Jan 2020 21:41:52 -0500</pubDate><guid>https://miterion.de/project/django/</guid><description>&lt;p&gt;During my time as a volunteer at the Computer Science Student Council at TU Darmstadt, I contributed to our web page projects.
Highlights include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Created an nametag creation and group assignment routine for the orientation week&lt;/li&gt;
&lt;li&gt;Implemented an automated access code distribution system for the course evaluation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also maintained these projects and made sure, that the automated dependency management and CI scripts were working and happy 😅.&lt;/p&gt;</description></item><item><title>Ansible Vaults with Gopass</title><link>https://miterion.de/post/ansible-gopass/</link><pubDate>Tue, 01 Oct 2019 12:30:13 +0200</pubDate><guid>https://miterion.de/post/ansible-gopass/</guid><description>&lt;h2 id="introduction-vault-ids"&gt;Introduction Vault IDs&lt;/h2&gt;
&lt;p&gt;The main ansible commands &lt;code&gt;ansible-vault&lt;/code&gt; and &lt;code&gt;ansible-playbook&lt;/code&gt; already support the &lt;code&gt;--vault-id&lt;/code&gt; parameter.
It requires a compound of two things: the vault ID label (e.g. dev, prod, etc.) and the means of getting the password
(e.g. &lt;code&gt;prompt&lt;/code&gt;, a password file or an executable).
The vault ID label can be specified while creating the vault or later by modifying the encrypted variant. In the following
example the label is called &lt;code&gt;vault-id-label&lt;/code&gt;. Notice the format version of 1.2, which you might need to change in your file too.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ANSIBLE_VAULT;1.2;AES256;vault-id-label
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;During an ansible run this vault could be opened by adding the &lt;code&gt;--vault-id vault-id-label@prompt&lt;/code&gt; parameter, which would prompt the
user for the vault password. Changing &lt;code&gt;prompt&lt;/code&gt; to &lt;code&gt;vaultpass&lt;/code&gt; would let ansible load the password from the file vaultpass.&lt;/p&gt;
&lt;h2 id="password-scripts"&gt;Password scripts&lt;/h2&gt;
&lt;p&gt;The third option would be to specify an executable, which will get called by ansible. There are two variants:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Normal scripts, which are called without any parameters and just return a password&lt;/li&gt;
&lt;li&gt;Client scripts, which have to end in client and are called with the &lt;code&gt;--vault-id&lt;/code&gt; option plus the label of the vault&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this example I will use a client script, since I have several vaults with different labels/passwords. The implementation is quite
straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ch"&gt;#!/usr/bin/env python&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;subprocess&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STDOUT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;VAULT_PASSWORD_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pathToMyAnsibleVaultPasswords/&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--vault-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Necessary argument &amp;#34;--vault-id&amp;#34; missing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gopass&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VAULT_PASSWORD_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;STDOUT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PIPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save this script as &lt;code&gt;gopass-client.py&lt;/code&gt; next to your ansible.cfg (also make it executable) and use &lt;code&gt;--vault-id vault-label@gopass-client&lt;/code&gt; in your ansible
command and it will magically unlock your vault using gopass. Don&amp;rsquo;t forget to change the &lt;code&gt;VAULT_PASSWORD_LOCATION&lt;/code&gt; to the path of your ansible passswords in your
gopass store.&lt;/p&gt;</description></item><item><title>FB20 Software Development</title><link>https://miterion.de/project/fb20softwaredevelopment/</link><pubDate>Sat, 23 Feb 2019 22:04:15 -0500</pubDate><guid>https://miterion.de/project/fb20softwaredevelopment/</guid><description>&lt;p&gt;During my time at the Computer Science department at the TU Darmstadt, I worked on several internal closed-source projects, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kubernetes Ansible Roles&lt;/li&gt;
&lt;li&gt;Hugo Website Themes&lt;/li&gt;
&lt;li&gt;Evaluation of Matlab and Toolkit Usage at the department&lt;/li&gt;
&lt;li&gt;User Management Website in Django&lt;/li&gt;
&lt;li&gt;Created specialized Containers for CI usage&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miterion.de/project/dfnclient"&gt;dfnclient&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For references, please contact &lt;a href="https://www.informatik.tu-darmstadt.de/fb20/organisation_fb20/infrastruktur_und_studentischer_poolservice/index.en.jsp" target="_blank" rel="noopener"&gt;ISP CS TU Darmstadt&lt;/a&gt;&lt;/p&gt;</description></item><item><title>dfnclient</title><link>https://miterion.de/project/dfnclient/</link><pubDate>Tue, 01 Jan 2019 16:21:14 -0500</pubDate><guid>https://miterion.de/project/dfnclient/</guid><description>&lt;h2 id="project-description"&gt;Project Description&lt;/h2&gt;
&lt;p&gt;Reverse-Engineered the Certificate API for the &lt;a href="https://www.dfn.de/en/" target="_blank" rel="noopener"&gt;German Research Network&lt;/a&gt;, so SSL certificates can be requested with one command.
Currently used by a number of German universities such as the TU Darmstadt, University of Heidelberg and TU Chemnitz.&lt;/p&gt;</description></item><item><title>Using Visual Studio Code with Okular and synctex</title><link>https://miterion.de/post/vscodeplusokular/</link><pubDate>Mon, 24 Dec 2018 00:40:17 +0100</pubDate><guid>https://miterion.de/post/vscodeplusokular/</guid><description>&lt;hr&gt;
&lt;p&gt;Visual Studio Code is a great editor, Okular a great PDF Viewer. To make them work together, a little bit of config is necessary though.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: Dan MacKinlay has written a nice introduction to Visual Studio Code as a LaTeX IDE. You can find
his article &lt;a href="https://livingthing.danmackinlay.name/vs_code_for_latex.html" target="_blank" rel="noopener"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently I started to use &lt;a href="https://code.visualstudio.com/" target="_blank" rel="noopener"&gt;Visual Studio Code&lt;/a&gt; more and more when
I have to write LaTeX code. Together with the &lt;a href="https://marketplace.visualstudio.com/items?itemName=James-Yu.latex-workshop" target="_blank" rel="noopener"&gt;LaTeX Workshop Extension&lt;/a&gt;,
creating beautiful documents is a breeze, since it has all the features of a good IDE.
Except one&amp;hellip; &lt;em&gt;Synctex&lt;/em&gt; &amp;#x1f644;&lt;/p&gt;
&lt;h2 id="the-problem-with-pdfjs"&gt;The problem with pdf.js&lt;/h2&gt;
&lt;p&gt;If you use the built in pdf viewer, Visual Studio Code (or as I have recently taken to calling it VCS &amp;#x1f605;)
uses pdf js, a web based pdf viewer written by Mozilla. Though this is sufficient for some use cases, it breaks
on several occasions like rendering a math formula. I therefore started using the external viewer function of the extension
to mitigate such problems (namely Okular). This leads to another problem: How to use synctex with it?&lt;/p&gt;
&lt;p&gt;Okular has a feature to jump to the correct location of a pdf, but there is one problem: Nobody ever bothered to write down the
syntax for this command. So after a long Google search and several trial and error attempts low and behold the VCS config for okular and synctex. Just
copy this into your user settings.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;latex-workshop.view.pdf.external.command&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;okular&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--unique&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;%PDF%&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// If you want the external viewer to be default, uncomment the next line
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//&amp;#34;latex-workshop.view.pdf.viewer&amp;#34;: &amp;#34;external&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;latex-workshop.view.pdf.external.synctex&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;command&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;okular&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;--unique&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;%PDF%#src:%LINE%%TEX%&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Yubikey and systemd</title><link>https://miterion.de/post/yubi-systemd/</link><pubDate>Fri, 22 Jun 2018 23:33:47 +0200</pubDate><guid>https://miterion.de/post/yubi-systemd/</guid><description>&lt;h2 id="background"&gt;Background&lt;/h2&gt;
&lt;p&gt;I love my Yubikey. Besides using it for local authentication it also contains
my PGP key. By using the awesome pass password manager (or more accurately the go rewrite &lt;a href="https://github.com/cortex/gopass" target="_blank" rel="noopener"&gt;gopass&lt;/a&gt;)
I do not have to write passwords into config files any more but just configure the correct call
to pass and the rest is handled by the system.&lt;/p&gt;
&lt;p&gt;In case of email this worked really well as long as I was using offlineimap. This program provides
a background daemon which downloads all your mails to your drive. I was really happy with this setup, but
as more and more accounts got added this was one of the biggest resource hogs on my system.&lt;/p&gt;
&lt;p&gt;So I decided to switch to &lt;a href="http://isync.sourceforge.net/" target="_blank" rel="noopener"&gt;isync&lt;/a&gt;, which is a lightweight and really fast alternative
to offlineimap. The problem is, that isync has to be called periodically since it just provides
a simple sync command. That means my password is needed every X minutes. Usually this is no problem, since my Yubikey
handles this for me. But sometimes I use my computer without it and this is a big problem.&lt;/p&gt;
&lt;p&gt;Isync attempts to decrypt the gpg file containing my password and naturally asks for the key. Since it is
saved on my Yubikey I get this handsome dialog:&lt;/p&gt;
&lt;figure id="figure-please-insert-card-with-serial-number"&gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img alt="Please insert card with serial number" srcset="
/post/yubi-systemd/nagdialog_hu_fac9c030d8ecb5c1.webp 400w,
/post/yubi-systemd/nagdialog_hu_2e5a296d47284340.webp 760w,
/post/yubi-systemd/nagdialog_hu_6b780fa6679c5b91.webp 1200w"
src="https://miterion.de/post/yubi-systemd/nagdialog_hu_fac9c030d8ecb5c1.webp"
width="511"
height="184"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Please insert card with serial number
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I can manually disable the systemd timer I wrote to trigger the sync but this is cumbersome. So I searched for a way
to automate this and found it:&lt;/p&gt;
&lt;h2 id="udev-to-the-rescue"&gt;Udev to the rescue&lt;/h2&gt;
&lt;p&gt;For some time now linux includes udev which is according to wikipedia&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a device manager for the Linux kernel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is a complicated system and I only want to focus on the user facing part, called rules. These are simple text files
which are evaluated by udev when hardware events trigger. A simple example is the addition of a usb keyboard which triggers
a command to use the built in keyboard.&lt;/p&gt;
&lt;h2 id="my-very-first-udev-rule"&gt;My very first udev rule&lt;/h2&gt;
&lt;p&gt;So I started to write my first udev rule. This is surprisingly easy, as a rule consists of a identifying
part and a execution part. First I had to find something to identify my key. I decided to use the vendor and
product id, which a USB device should usually have. They can easily be extracted by using the &lt;code&gt;lsusb&lt;/code&gt; command.
For my key the output was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bus 001 Device 030: ID 1050:0407 Yubico.com Yubikey 4 OTP+U2F+CCID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The format is: ID &amp;lt;vendor id&amp;gt;:&amp;lt;device id&amp;gt;.&lt;/p&gt;
&lt;p&gt;With this in mind my udev rule looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SUBSYSTEM==&amp;#34;usb&amp;#34;, ENV{ID_MODEL_ID}=&amp;#34;0407&amp;#34;, ENV{ID_VENDOR_ID}=&amp;#34;1050&amp;#34;, TAG+=&amp;#34;systemd&amp;#34;, SYMLINK=&amp;#34;yubikey&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SYSTEMD_READY=0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
Breaking this in its parts you get:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SUBSYSTEMS==&amp;quot;usb&amp;quot;&lt;/code&gt;: limits to usb devices&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ENV{ID_MODEL_ID}==&amp;quot;0407&amp;quot;, ENV{ID_VENDOR_ID}==&amp;quot;1050&amp;quot;,&lt;/code&gt;: limit to the specified usb prodcut and vendor id&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYMLINK=&amp;quot;yubikey&amp;quot;&lt;/code&gt;: create a device called &amp;ldquo;yubikey&amp;rdquo; in dev (/dev/yubikey)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TAG+=&amp;quot;systemd&amp;quot;&lt;/code&gt;: only devices with this tag are added to the systemd device system&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYSTEMD_READY=0&lt;/code&gt;: only show this device in the tree if it is actually plugged in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Saving this in a file called &lt;code&gt;85-yubikey.rules&lt;/code&gt; inside the &lt;code&gt;/etc/udev/rules.d/&lt;/code&gt; directory and calling
&lt;code&gt;sudo udevadm control --reload-rules&lt;/code&gt; activates the rule (the number in front of the rule just specifies
the order; this rule gets loaded pretty late and after all rules with lower front numbers).&lt;/p&gt;
&lt;p&gt;If you plug your device in now a new entry in &lt;code&gt;/dev/&lt;/code&gt; should appear, which is called &lt;code&gt;yubico&lt;/code&gt;. If this does not
happen, check the ids and maybe restart your system.&lt;/p&gt;
&lt;h2 id="telling-systemd-to-look-for-the-key"&gt;Telling systemd to look for the key&lt;/h2&gt;
&lt;p&gt;The rest is really simple. Systemd generates &lt;code&gt;dev-devicename.device&lt;/code&gt; files for every udev device with the systemd tag.
This device files can be used everywhere where a unit or target is required.&lt;/p&gt;
&lt;p&gt;A quick look in the documentation suggests &lt;code&gt;Requisite&lt;/code&gt; and &lt;code&gt;After&lt;/code&gt; as the appropriate options:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Requisite=dev-yubikey.device
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;After=dev-yubikey.device
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is basically the equivalent to the following meme &amp;#x1f609;
&lt;figure &gt;
&lt;div class="d-flex justify-content-center"&gt;
&lt;div class="w-100" &gt;&lt;img alt="" srcset="
/post/yubi-systemd/isthis_hu_afb720a42d7fe486.webp 400w,
/post/yubi-systemd/isthis_hu_8685d9db0309493a.webp 760w,
/post/yubi-systemd/isthis_hu_ce07f294de74b0db.webp 1200w"
src="https://miterion.de/post/yubi-systemd/isthis_hu_afb720a42d7fe486.webp"
width="556"
height="500"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;And this is it. Now the service is only started when the key is inserted. This all took about half an hour and will
save me a lot of time and nerves in the future.&lt;/p&gt;</description></item><item><title>Ansible Roles</title><link>https://miterion.de/project/ansible/</link><pubDate>Mon, 19 Jun 2017 21:54:18 -0500</pubDate><guid>https://miterion.de/project/ansible/</guid><description>&lt;p&gt;Worked on several Ansible roles for both the CS department and student council.
Some roles I wrote included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BigBlueButton (Video Conferencing Software)&lt;/li&gt;
&lt;li&gt;Bookstack (Wiki)&lt;/li&gt;
&lt;li&gt;Mailman3 (Mailinglist Software)&lt;/li&gt;
&lt;li&gt;Prometheus (Alerting and Monitoring Tool)&lt;/li&gt;
&lt;li&gt;nextcloud (Document Storage)&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title/><link>https://miterion.de/admin/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://miterion.de/admin/</guid><description/></item></channel></rss>