<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Aapje is Baas</title>
    <link>https://aapjeisbaas.nl/</link>
      <atom:link href="https://aapjeisbaas.nl/index.xml" rel="self" type="application/rss+xml" />
    <description>Aapje is Baas</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><lastBuildDate>Tue, 18 Aug 2020 11:16:15 +0200</lastBuildDate>
    <image>
      <url>https://aapjeisbaas.nl/img/logo-aapjeisbaas.png</url>
      <title>Aapje is Baas</title>
      <link>https://aapjeisbaas.nl/</link>
    </image>
    
    <item>
      <title>PDF Network Printing Without Drivers</title>
      <link>https://aapjeisbaas.nl/post/pdf-network-printing-without-drivers/</link>
      <pubDate>Tue, 18 Aug 2020 11:16:15 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/pdf-network-printing-without-drivers/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve created a small docker container that exposes an API and a web interface to receive pdf files and forward them to an IPP printer on the network.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&#34;https://hub.docker.com/r/aapjeisbaas/pdf-to-ipp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Dockerhub&lt;/a&gt;

&lt;a href=&#34;https://gitlab.com/aapjeisbaas/pdf-to-ipp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitLab Source Code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you like this project, please star it on GitLab and Dockerhub.&lt;/p&gt;
&lt;h2 id=&#34;why-build-something-like-this&#34;&gt;Why build something like this?&lt;/h2&gt;
&lt;p&gt;I needed an easy way for me as a human to print PDF files from all sorts of devices before I created this I used google CloudPrint which was great but they are 
&lt;a href=&#34;https://support.google.com/chrome/a/answer/9633006?hl=en&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;shutting down the service&lt;/a&gt;.
Also, it wasn&amp;rsquo;t flexible to be used with random scripts without jumping through complicated authentication hoops so I&amp;rsquo;ve created this &lt;em&gt;simple&lt;/em&gt; http app that can run anywhere and point to any ipp printer it can reach.
It uses 
&lt;a href=&#34;https://www.cups.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;cups&lt;/a&gt; to setup the connection to the printer and issue commands over 
&lt;a href=&#34;https://www.pwg.org/ipp/everywhere.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;IPP&lt;/a&gt; there is a simple check in the python server that verifies that it is a usable PDF and that the size is not too small with 
&lt;a href=&#34;https://github.com/mstamy2/PyPDF2&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PyPDF2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It is probably as plug and play as it gets, although I&amp;rsquo;m not entirely sure because I have only 1 network printer that I can test with.&lt;/p&gt;
&lt;h2 id=&#34;how-to-use&#34;&gt;How to use?&lt;/h2&gt;
&lt;p&gt;Run the container and place your printer&amp;rsquo;s ip in the env var &lt;code&gt;IPP_IP&lt;/code&gt; like in this example docker run command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -d -p 5000:5000 -e &amp;quot;IPP_IP=192.168.1.66&amp;quot; aapjeisbaas/pdf-to-ipp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can reach the web interface of the service at: http://127.0.0.1:5000/&lt;/p&gt;
&lt;h2 id=&#34;automate-all-the-things&#34;&gt;Automate all the things!&lt;/h2&gt;
&lt;p&gt;This is pretty useful by itself but if you&amp;rsquo;re anything like me you want to automate your printing.
The application expects A4 pdf files to be uploaded to: &lt;code&gt;http://127.0.0.1:5000/uploader&lt;/code&gt; in a python function this will look like the one below.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;
import requests

def send_to_printer(pdf):
    files = {&#39;file&#39;: open(pdf, &#39;rb&#39;)}
    requests.post(&#39;http://127.0.0.1:5000/uploader&#39;, files=files)

send_to_printer(&#39;/home/user/pdf/print-this.pdf&#39;)

&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;It is cross-build to support all architectures I could think of and I run it on an ARM64 K3S cluster with raspberry pi&amp;rsquo;s and the container uses about 50MB of RAM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;this-does-not-do-x-y-or-z&#34;&gt;This does not do X, Y OR Z&lt;/h2&gt;
&lt;p&gt;Nope, and probably never will.&lt;/p&gt;
&lt;p&gt;This is mostly meant as a jumping-off point to &amp;ldquo;roll your own&amp;rdquo; and learn a bit about python/docker/cups/ipp along the way.
It is not ultra configurable, nor do I need it to be.
This thing just prints packing slips for my wife&amp;rsquo;s webshop and don&amp;rsquo;t want to overcomplicate things by adding to much &amp;ldquo;enterprise&amp;rdquo; sauce.&lt;/p&gt;
&lt;h2 id=&#34;do-you-have-more-printing-projects&#34;&gt;Do you have more printing projects?&lt;/h2&gt;
&lt;p&gt;Well, yes and it is even more niche than this one.
This project is based on 
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/label-container&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;label-container&lt;/a&gt; and it takes in, you&amp;rsquo;ve probably guessed it already: PDF shipping labels, and it prints them on a 
&lt;a href=&#34;https://www.dymo.com/en-US/labelwriter-4xl-label-printer&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Dymo 4XL&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Problem-solving with lambda</title>
      <link>https://aapjeisbaas.nl/post/problem-solving-with-lambda/</link>
      <pubDate>Mon, 10 Aug 2020 16:20:13 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/problem-solving-with-lambda/</guid>
      <description>&lt;p&gt;My background is not predominantly programming I&amp;rsquo;ve spent most of my time as a system/infra/cloud/storage/networking engineer.
Like most in my profession can tell you, we have 2 main tasks; &lt;strong&gt;build it&lt;/strong&gt; &amp;amp; &lt;strong&gt;keep it online&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When you spend any time at a helpdesk type job and you&amp;rsquo;ll be well versed in solving a wide range of problems that customers face, that&amp;rsquo;s actually how this blog started.
This makes you a creative and fast problem solver and if you&amp;rsquo;re lucky you&amp;rsquo;ll get enough edge cases you&amp;rsquo;ve never seen before to add to your arsenal of knowledge and keep yourself from going insane.&lt;/p&gt;
&lt;p&gt;The takeaway from all of this is that you build muscle memory in fixing these problems.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here&amp;rsquo;s my fix anything workflow:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Start with the point that was noticed by the client or monitoring&lt;/li&gt;
&lt;li&gt;Figure out what delivered that thing that doesn&amp;rsquo;t work&lt;/li&gt;
&lt;li&gt;Follow the break as deep as you need to go&lt;/li&gt;
&lt;li&gt;Start fixing from there and follow all the way up&lt;/li&gt;
&lt;li&gt;Repeat until you can verify the noticed thing to be working again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recently I realized that if you remove the deep dive into the root cause of the problem and replace it with a need for a solution and drill down to the core functionality you end up at the same place in the middle.
From this point you then fix or build your way back up to the user-visible layers, this strategy is how I now build most of my lambda solutions.&lt;/p&gt;
&lt;div class=&#34;alert alert-note&#34;&gt;
  &lt;div&gt;
    &amp;ldquo;Start from the core problem and work out towards the user from there&amp;rdquo;
  &lt;/div&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&#34;what-is-lambda&#34;&gt;What is lambda?&lt;/h2&gt;
&lt;p&gt;AWS Lambda is an 
&lt;a href=&#34;https://en.wikipedia.org/wiki/Event-driven_programming&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;event-driven&lt;/a&gt;, serverless computing platform that aims to make it possible to deploy code in a smaller form factor and makes it possible to ship only a single function.
The Lambda platform handles the interaction with the events and runs your code only when necessary, this makes it super simple to integrate with other components and it is only billed for the time your code is running.
When your code is triggered it initializes a small container that runs your code, the items you have in memory that you initialize will stay available for a few minutes (as long as the container stays online) this enables you to make subsequent invocations faster.
Lambda supports multiple languages I&amp;rsquo;ll be using python in this guide, for a full list of supported runtimes check out 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the lambda documentation&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;writing-python-lambda-solutions&#34;&gt;Writing python lambda solutions&lt;/h2&gt;
&lt;p&gt;So let&amp;rsquo;s get to building some python code.&lt;/p&gt;
&lt;p&gt;First up create a fresh virtualenv, you might want to use a Python virtualenv manager if you&amp;rsquo;re lazy like me use this: 
&lt;a href=&#34;https://github.com/MichaelAquilina/zsh-autoswitch-virtualenv&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;zsh-autoswitch-virtualenv&lt;/a&gt;
This gives you a separate space for python packages only for this project.&lt;/p&gt;
&lt;h3 id=&#34;1-virtualenv-setup&#34;&gt;1. virtualenv setup&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# svanbroekhoven @ lynx in ~/git/tst [0:54:57] 
$ mkvenv
Creating tst virtualenv
created virtual environment CPython3.8.3.final.0-64 in 147ms
  creator CPython3Posix(dest=/home/svanbroekhoven/.virtualenvs/tst, clear=False, global=False)
  seeder FromAppData(download=False, pip=latest, setuptools=latest, wheel=latest, via=copy, app_data_dir=/home/svanbroekhoven/.local/share/virtualenv/seed-app-data/v1.0.1)
  activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
Switching virtualenv: tst [🐍Python 3.8.3]
(tst) 
# svanbroekhoven @ lynx in ~/git/tst [0:55:03] 
$ cd ..
Deactivating: tst

# svanbroekhoven @ lynx in ~/git [1:53:20] 
$ cd tst          
Switching virtualenv: tst [🐍Python 3.8.3]
(tst) 
# svanbroekhoven @ lynx in ~/git/tst [1:53:27] 
$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see in the example above, a virtualenv switcher helps if you&amp;rsquo;re working on multiple projects.&lt;/p&gt;
&lt;h3 id=&#34;2-try-to-solve-it-once&#34;&gt;2. try to solve it once&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Core problem: I need the &lt;em&gt;n&lt;/em&gt;th Fibonacci number on many places where I don&amp;rsquo;t have much compute power or local storage to store enough of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s open up a python shell or 
&lt;a href=&#34;https://realpython.com/jupyter-notebook-introduction/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;jupyter notebook&lt;/a&gt; and a web browser to try things and research possible solutions,
in this example I&amp;rsquo;ll be using the example of a 
&lt;a href=&#34;https://en.wikipedia.org/wiki/Fibonacci_number#Sequence_properties&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;fibonacci number&lt;/a&gt; returning API.
As a first project I would try to avoid complex dependencies outside of pure python, as a rough guide this means no Audio, Video and image-based projects.
These libraries often require system-level dependencies which we can only install through building a lambda layer and that is a bit too involved for this guide.
&lt;em&gt;If you do want to dive into building layers check out 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/python-package.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this article&lt;/a&gt; in the lambda documentation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The most basic Fibonacci function you can write looks like this, it has many problems but that leaves us some room for improvement in the future.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

for n in range(10 + 1):
    print(str(n)+  &amp;quot;\t&amp;quot; + str(fibonacci(n)))
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;3-build-and-tweak-your-mainpy&#34;&gt;3. Build and tweak your main.py&lt;/h3&gt;
&lt;p&gt;After some tinkering our basic script and function it looks like the one above, now my favorite way to proceed from here in put it in a &lt;code&gt;main.py&lt;/code&gt; script and run it interactive like this: &lt;code&gt;python -i main.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# svanbroekhoven @ lynx in ~ [14:28:01] C:127
$ cd git/proza/content/post/problem-solving-with-lambda 
Switching virtualenv: problem-solving-with-lambda [Python 3.8.3]
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [14:28:16] 
$ cat main.py 
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

for n in range(10 + 1):
    print(str(n)+  &amp;quot;\t&amp;quot; + str(fibonacci(n)))
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [14:28:59] 
$ python -i main.py
0	0
1	1
2	1
3	2
4	3
5	5
6	8
7	13
8	21
9	34
10	55
&amp;gt;&amp;gt;&amp;gt; dir()
[&#39;__annotations__&#39;, &#39;__builtins__&#39;, &#39;__doc__&#39;, &#39;__loader__&#39;, &#39;__name__&#39;, &#39;__package__&#39;, &#39;__spec__&#39;, &#39;fibonacci&#39;, &#39;n&#39;]
&amp;gt;&amp;gt;&amp;gt; n
10   
&amp;gt;&amp;gt;&amp;gt; fibonacci(n)
55
&amp;gt;&amp;gt;&amp;gt; fibonacci(20)
6765
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way we can play with what we created without the need to paste our code in the interactive shell, this way we can hack on it and incorporate our first function in some other loops to validate if it&amp;rsquo;ll fit our requirements. Below I demonstrate my experiments in the interactive shell to try and show how I use this process.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# svanbroekhoven @ lynx in ~ [14:28:01] C:127
$ cd git/proza/content/post/problem-solving-with-lambda 
Switching virtualenv: problem-solving-with-lambda [Python 3.8.3]
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [14:28:16] 
$ cat main.py 
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

for n in range(10 + 1):
    print(str(n)+  &amp;quot;\t&amp;quot; + str(fibonacci(n)))
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [14:28:59] 
$ python -i main.py
0	0
1	1
2	1
3	2
4	3
5	5
6	8
7	13
8	21
9	34
10	55
&amp;gt;&amp;gt;&amp;gt; dir()
[&#39;__annotations__&#39;, &#39;__builtins__&#39;, &#39;__doc__&#39;, &#39;__loader__&#39;, &#39;__name__&#39;, &#39;__package__&#39;, &#39;__spec__&#39;, &#39;fibonacci&#39;, &#39;n&#39;]
&amp;gt;&amp;gt;&amp;gt; n
10
&amp;gt;&amp;gt;&amp;gt; fibonacci(n)
55
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That was the fix once side over now let&amp;rsquo;s do it in a loop and display the input and output to the user.
You can read all the &lt;code&gt;KeyboardInterrupt&lt;/code&gt; as the operation took too long, I killed it to keep working instead of waiting on it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for n in range(10, 200):
...   fibonacci(n)
... 
55
89
144
 ...
 ...
 ...
5702887
9227465
^C
KeyboardInterrupt
&amp;gt;&amp;gt;&amp;gt; for n in range(10, 200):
...   print( str(n) + &amp;quot;: &amp;quot; + str(fibonacci(n)))
... 
10: 55
11: 89
 ...
 ...
 ...
36: 14930352
37: 24157817
^C
KeyboardInterrupt
&amp;gt;&amp;gt;&amp;gt; import random
&amp;gt;&amp;gt;&amp;gt; random.randrange(10)
6
&amp;gt;&amp;gt;&amp;gt; 
&amp;gt;&amp;gt;&amp;gt; 
&amp;gt;&amp;gt;&amp;gt; for i in range(10):
...   n = random.randrange(20)
...   print( str(n) + &amp;quot;: &amp;quot; + str(fibonacci(n)))
... 
3: 2
5: 5
5: 5
12: 144
15: 610
1: 1
1: 1
13: 233
4: 3
11: 89
&amp;gt;&amp;gt;&amp;gt; for i in range(10):
...   n = random.randrange(200)
...   print( str(n) + &amp;quot;: &amp;quot; + str(fibonacci(n)))
... 

^C
KeyboardInterrupt
&amp;gt;&amp;gt;&amp;gt; from datetime import datetime, timedelta 
&amp;gt;&amp;gt;&amp;gt; now = datetime.now()
&amp;gt;&amp;gt;&amp;gt; start = datetime.now()
&amp;gt;&amp;gt;&amp;gt; delta = datetime.now() - start 
&amp;gt;&amp;gt;&amp;gt; delta
datetime.timedelta(seconds=37, microseconds=994986)
&amp;gt;&amp;gt;&amp;gt; print(delta)
0:00:37.994986
&amp;gt;&amp;gt;&amp;gt; for i in range(10):
...   start = datetime.now()
...   n = random.randrange(50)
...   print( str(n) + &amp;quot;: &amp;quot; + str(fibonacci(n)) + &amp;quot; &amp;quot; + str(datetime.now() - start))
... 
41: 165580141 0:00:43.840478
3: 2 0:00:00.000009
9: 34 0:00:00.000011
^C
KeyboardInterrupt
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I&amp;rsquo;ve accomplished this far is a simple loop that prints out 10 random fib numbers in a range from 0 to 50 and print time taken to complete it, but the results are really slow so I needed a built-in stopwatch to do some crude benchmarking of the function. I now start with placing some of my experiments back into the main.py with vim and make sure this &amp;ldquo;benchmark&amp;rdquo; auto starts as I run the file. I&amp;rsquo;ll add a lru_cache to the function to cache results to hide the slowness and make the output a bit more readable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:06:00] 
$ vim main.py      
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:06:06] 
$ python -i main.py
38: 39088169 0:00:00.000024
14: 377 0:00:00.000002
10: 55 0:00:00.000002
26: 121393 0:00:00.000001
17: 1597 0:00:00.000002
29: 514229 0:00:00.000001
30: 832040 0:00:00.000001
46: 1836311903 0:00:00.000005
4: 3 0:00:00.000001
28: 317811 0:00:00.000001
&amp;gt;&amp;gt;&amp;gt; 
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:06:23] 
$ vim main.py      
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:07:45] 
$ python -i main.py
11	0:00:00.000005 89
4	0:00:00.000002 3
17	0:00:00.000001 1597
30	0:00:00.000001 832040
47	0:00:00.000001 2971215073
38	0:00:00.000001 39088169
35	0:00:00.000002 9227465
44	0:00:00.000001 701408733
2	0:00:00.000001 1
1	0:00:00.000001 1
&amp;gt;&amp;gt;&amp;gt; 
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:08:05] 
$ vim main.py      
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:08:17] 
$ python -i main.py
67	0:00:00.000007 44945570212853
20	0:00:00.000004 6765
72	0:00:00.000002 498454011879264
79	0:00:00.000001 14472334024676221
27	0:00:00.000002 196418
68	0:00:00.000002 72723460248141
33	0:00:00.000002 3524578
10	0:00:00.000002 55
17	0:00:00.000002 1597
90	0:00:00.000001 2880067194370816120
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well this looks kinda oke, let&amp;rsquo;s get rid of the auto run benchmark crap and add a function that triggers the benchmark with a range variable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:08:23] 
$ vim main.py      
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:09:33] 
$ python -i main.py
&amp;gt;&amp;gt;&amp;gt; dir()
[&#39;__annotations__&#39;, &#39;__builtins__&#39;, &#39;__doc__&#39;, &#39;__loader__&#39;, &#39;__name__&#39;, &#39;__package__&#39;, &#39;__spec__&#39;, &#39;datetime&#39;, &#39;fibonacci&#39;, &#39;lru_cache&#39;, &#39;random&#39;, &#39;range_test&#39;]
&amp;gt;&amp;gt;&amp;gt; range_test(100)
61	0:00:00.000066 2504730781961
50	0:00:00.000017 12586269025
61	0:00:00.000010 2504730781961
93	0:00:00.000010 12200160415121876738
2	0:00:00.000015 1
17	0:00:00.000011 1597
82	0:00:00.000011 61305790721611591
15	0:00:00.000011 610
65	0:00:00.000011 17167680177565
61	0:00:00.000006 2504730781961
&amp;gt;&amp;gt;&amp;gt; range_test(500)
262	0:00:00.000029 2542592393026885507715496646813780220945054040571721231
468	0:00:00.000016 28624020537229717283244863695841661789309459371299941322398704536965075971940465647510004531000816
424	0:00:00.000011 18250487254938611555074410636524312658020849229014745928158881386149462839394269270468043
237	0:00:00.000006 15156039800290547036315704478931467953361427680642
496	0:00:00.000006 20341574322680408081083829243820203612317308197211964554628215486203974898255803242740333222721700974747
402	0:00:00.000014 460835978753503578226215883073872246385764472086797082873203188542544616448248343576
286	0:00:00.000006 263621064469290555679241849789653324393054271110084140201023
387	0:00:00.000006 337856107814181089864066841370437948648180483483258553487263501935155470092051458
24	0:00:00.000005 46368
371	0:00:00.000005 153083904475345790698149223310665389766178449653686710164582374234640876900329
&amp;gt;&amp;gt;&amp;gt; 
(problem-solving-with-lambda) 
# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [15:10:09] 
$ cat main.py 
from functools import lru_cache
from datetime import datetime
import random

@lru_cache(maxsize = None)
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

def range_test(nMax):
    for i in range(10):
      start = datetime.now()
      n = random.randrange(nMax)
      print( str(n) + &amp;quot;\t&amp;quot; + str(datetime.now() - start) + &amp;quot; &amp;quot; + str(fibonacci(n)))

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I discovered that it was way to slow and by adding a 
&lt;a href=&#34;https://docs.python.org/3/library/functools.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;lru_cache&lt;/code&gt;&lt;/a&gt; to the function it became a bit more responsive and usable for our purpose.
Also I&amp;rsquo;ve changed the old range test a new variable &lt;code&gt;nMax&lt;/code&gt; version with random numbers and a time spent counter.
Now for a final cherry on top let&amp;rsquo;s add a 
&lt;a href=&#34;https://docs.python.org/3/library/__main__.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;main&lt;/a&gt;, if you&amp;rsquo;re familiar with other programming languages this is probably what you&amp;rsquo;re used to but in python it is not strictly necessary.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;if __name__ == &amp;quot;__main__&amp;quot;:
    # execute only if run as a script
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above will only be triggered if the script is executed directly and not when the file is imported.
Let&amp;rsquo;s adapt it to our situation and run a simple benchmark when started directly as a script.
The main snippet is always placed at the bottom of our script as all the functions need to placed in memory before we can call them in our main.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from functools import lru_cache
from datetime import datetime
import random

@lru_cache(maxsize = None)
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

def range_test(nMax):
    for i in range(10):
      start = datetime.now()
      n = random.randrange(nMax)
      print( str(n) + &amp;quot;\t&amp;quot; + str(datetime.now() - start) + &amp;quot; &amp;quot; + str(fibonacci(n)))

if __name__ == &amp;quot;__main__&amp;quot;:
    # execute only if run as a script
    range_test(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This wil give us the following result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [10:35:55] 
$ python main.py 
96      0:00:00.000005 51680708854858323072
58      0:00:00.000003 591286729879
53      0:00:00.000001 53316291173
0       0:00:00.000001 0
12      0:00:00.000001 144
45      0:00:00.000001 1134903170
32      0:00:00.000001 2178309
73      0:00:00.000001 806515533049393
97      0:00:00.000001 83621143489848422977
44      0:00:00.000001 701408733

# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [10:35:59] 
$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now this isn&amp;rsquo;t very useful, let&amp;rsquo;s add some cli params to make it calculate a Fibonacci number.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;if __name__ == &amp;quot;__main__&amp;quot;:
    # User input handling
    import argparse
    parser = argparse.ArgumentParser(description=&amp;quot;Returns the n&#39;th fibonacci number.&amp;quot;)
    parser.add_argument(&#39;-n&#39;, required=True, type=int, help=&amp;quot;which number in the fibonacci sequence you want&amp;quot;)
    args = parser.parse_args()

    # execute calculation
    print(fibonacci(args.n))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [10:44:07] 
$ python main.py        
usage: main.py [-h] -n N
main.py: error: the following arguments are required: -n

# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [10:44:34] C:2
$ python main.py -n 20 
6765

# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [10:47:35] 
$ python main.py -n 200
280571172992510140037611932413038677189525
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s yielding us a lot more value and a pleasant help text if we forget to pass a &lt;code&gt;-n #num&lt;/code&gt;, now let&amp;rsquo;s add our calculation part of the main block to a lambda handler function to give lambda a way to interact with it and add a 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;api gateway test event&lt;/a&gt; to test the 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;lambda_handler&lt;/a&gt; function.
I won&amp;rsquo;t go into details about this if you want to read more just follow the links below, for now we only care about the following location in the event: &lt;code&gt;queryStringParameters&lt;/code&gt;
All data is passed through in JSON so we will need to &lt;code&gt;import json&lt;/code&gt; at the top of our file.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I use the API Gateway implementation here, but there are many ways to trigger and each has it&amp;rsquo;s own event body please check the documentation to find out how to 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;use AWS Lambda with other services.&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def lambda_handler(event, context):
    #print(&amp;quot;Received event: &amp;quot; + json.dumps(event, indent=2))
    return fibonacci(int(event[&#39;queryStringParameters&#39;][&#39;n&#39;]))

# This represents an api gateway request to: https://sub.domain.tld/api/v1/fibonacci?n=100
test_event = json.loads(&amp;quot;&amp;quot;&amp;quot;{
  &amp;quot;path&amp;quot;: &amp;quot;/api/v1/fibonacci&amp;quot;,
  &amp;quot;httpMethod&amp;quot;: &amp;quot;GET&amp;quot;,
  &amp;quot;queryStringParameters&amp;quot;: {
    &amp;quot;n&amp;quot;: &amp;quot;100&amp;quot;
  },
  &amp;quot;pathParameters&amp;quot;: {
    &amp;quot;proxy&amp;quot;: &amp;quot;/fibonacci&amp;quot;
  },
  &amp;quot;requestContext&amp;quot;: {
    &amp;quot;identity&amp;quot;: {
      &amp;quot;sourceIp&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,
      &amp;quot;userAgent&amp;quot;: &amp;quot;Custom User Agent String&amp;quot;
    },
    &amp;quot;path&amp;quot;: &amp;quot;/prod/fibonacci&amp;quot;,
    &amp;quot;protocol&amp;quot;: &amp;quot;HTTP/1.1&amp;quot;
  }
}&amp;quot;&amp;quot;&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [11:24:40] 
$ python -i main.py -n 100
354224848179261915075
&amp;gt;&amp;gt;&amp;gt; lambda_handler(test_event, &#39;&#39;)
354224848179261915075
&amp;gt;&amp;gt;&amp;gt; fibonacci(100)
354224848179261915075
&amp;gt;&amp;gt;&amp;gt; 

# svanbroekhoven @ lynx in ~/git/proza/content/post/problem-solving-with-lambda on git:master x [11:25:21] 
$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now as a final step to complete our local lambda test we run our lambda handler from our main block with the test event, I also place the test event into the main block to reduce memory usage in lambda.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from functools import lru_cache
from datetime import datetime
import random
import json

@lru_cache(maxsize = None)
def fibonacci(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

def range_test(nMax):
    for i in range(10):
      start = datetime.now()
      n = random.randrange(nMax)
      print( str(n) + &amp;quot;\t&amp;quot; + str(datetime.now() - start) + &amp;quot; &amp;quot; + str(fibonacci(n)))

def lambda_handler(event, context):
    #print(&amp;quot;Received event: &amp;quot; + json.dumps(event, indent=2))
    return fibonacci(int(event[&#39;queryStringParameters&#39;][&#39;n&#39;]))


if __name__ == &amp;quot;__main__&amp;quot;:
    # User input handling
    import argparse
    parser = argparse.ArgumentParser(description=&amp;quot;Returns the n&#39;th fibonacci number.&amp;quot;)
    parser.add_argument(&#39;-n&#39;, required=True, type=int, help=&amp;quot;which number in the fibonacci sequence you want&amp;quot;)
    args = parser.parse_args()

    # execute calculation directly
    # print(fibonacci(args.n))

    # This represents an api gateway request to: https://sub.domain.tld/api/v1/fibonacci?n=100
    test_event = json.loads(&amp;quot;&amp;quot;&amp;quot;{
      &amp;quot;path&amp;quot;: &amp;quot;/api/v1/fibonacci&amp;quot;,
      &amp;quot;httpMethod&amp;quot;: &amp;quot;GET&amp;quot;,
      &amp;quot;queryStringParameters&amp;quot;: {
        &amp;quot;n&amp;quot;: &amp;quot;100&amp;quot;
      },
      &amp;quot;pathParameters&amp;quot;: {
        &amp;quot;proxy&amp;quot;: &amp;quot;/fibonacci&amp;quot;
      },
      &amp;quot;requestContext&amp;quot;: {
        &amp;quot;identity&amp;quot;: {
          &amp;quot;sourceIp&amp;quot;: &amp;quot;127.0.0.1&amp;quot;,
          &amp;quot;userAgent&amp;quot;: &amp;quot;Custom User Agent String&amp;quot;
        },
        &amp;quot;path&amp;quot;: &amp;quot;/prod/fibonacci&amp;quot;,
        &amp;quot;protocol&amp;quot;: &amp;quot;HTTP/1.1&amp;quot;
      }
    }&amp;quot;&amp;quot;&amp;quot;)

    # insert aur desired n in the test_event
    test_event[&#39;queryStringParameters&#39;][&#39;n&#39;] = args.n
    # execute calculation with test event through lambda_handler function
    print(lambda_handler(test_event, &#39;&#39;))

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;packaging-python-lambda-functions&#34;&gt;Packaging python lambda functions&lt;/h2&gt;
&lt;p&gt;To prepare our function to be able to run in Lambda we need to zip it together with it&amp;rsquo;s dependencies this sounds pretty easy, and guess what&amp;hellip; it is!
I use GitLab for remote git storage and their great CI/CD pipelines, my examples will be based around GitLab but you can use whatever you&amp;rsquo;re comfortable with.&lt;/p&gt;
&lt;p&gt;There are several ways to deploy your function:&lt;/p&gt;
&lt;h4 id=&#34;1-aws-web-console&#34;&gt;1. AWS Web Console&lt;/h4&gt;
&lt;p&gt;This is only usable if you have no external dependencies and small scripts, it is user friendly to setup but updating code will become a fragile human-driven process.&lt;/p&gt;
&lt;h4 id=&#34;2-aws-lambda-cli&#34;&gt;2. AWS Lambda CLI&lt;/h4&gt;
&lt;p&gt;This is often used in examples from AWS themselves, it is a lot better than copying and pasting in the web console but still not what I&amp;rsquo;d recommend for production use.
The syntax is quite clear and the 
&lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/reference/lambda/index.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;reference&lt;/a&gt; helps you to do everything you can think of with the lambda service.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# AWS Lambda CLI update code example
aws lambda update-function-code \
    --function-name  my-function \
    --zip-file fileb://my-function.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;3-aws-cloudformation&#34;&gt;3. AWS Cloudformation&lt;/h4&gt;
&lt;p&gt;This is my favorite way of deploying on AWS, it gives you full control over the recourses and makes expanding with other services like DynamoDB at a later point in time much easier since you&amp;rsquo;ve already build your infra as code pipeline.
This is also the way we&amp;rsquo;ll be deploying the code in this tutorial.&lt;/p&gt;
&lt;h4 id=&#34;4-aws-serverless-application-model&#34;&gt;4. AWS Serverless Application Model&lt;/h4&gt;
&lt;p&gt;From the brochure: &lt;strong&gt;Build serverless applications in simple and clean syntax&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To me this wasn&amp;rsquo;t a super intuitive method and more confusing because it isn&amp;rsquo;t just plain vanilla cloudformation and multi-account deployment isn&amp;rsquo;t supported out of the box. I did make a multi-account hack on top of sam but I wouldn&amp;rsquo;t recommend it.
If you&amp;rsquo;re still curious here is the 
&lt;a href=&#34;https://aws.amazon.com/serverless/sam/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;AWS SAM Overview page&lt;/a&gt; and a 
&lt;a href=&#34;https://github.com/awsdocs/aws-lambda-developer-guide/tree/master/sample-apps/blank-python&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;blank python sam project&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;cicd-packaging-and-deployment-with-gitlab-and-cloudformation&#34;&gt;CI/CD Packaging and deployment with Gitlab and Cloudformation&lt;/h3&gt;
&lt;p&gt;Before we take it apart step by step, this is what our Gitlab pipeline and cfn.yml (cloudformation) will look like.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;default:
  image: python

stages:
  - deployment

deployment:
  only:
    refs:
      - master
  stage: deployment
  artifacts:
    paths:
    - fibonacciPackage.zip
    expire_in: 1 week
  script: 
    - apt update -qqq &amp;amp;&amp;amp; apt install -qqq -y zip &amp;gt; /dev/null # pull in zip as it is not in the python default container
    - pip install --quiet awscli                             # need this for the upload but not in the package
    - pip install --quiet -r requirements.txt -t ./          # silently install requirements in the current dir
    - chmod -R 755 .
    - zip -r fibonacciPackage.zip . -x &amp;quot;.git*&amp;quot; --quiet
    - export zipname=&amp;quot;fibonacciPackage-$(date +%s).zip&amp;quot;
    - aws s3 cp fibonacciPackage.zip s3://bucket/lambda-packages/fibonacci/${zipname}
    - aws cloudformation deploy --template-file cfn.yml --capabilities CAPABILITY_IAM --stack-name fibonacci --no-fail-on-empty-changeset --parameter-overrides s3Bucket=&amp;quot;bucketname&amp;quot; s3Key=&amp;quot;lambda-packages/fibonacci/${zipname}&amp;quot; 
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;AWSTemplateFormatVersion: &#39;2010-09-09&#39;
Description: fibonacci service

Parameters:
  s3Bucket:
    Type: String
    Default: &amp;quot;bucket&amp;quot;
  s3Key:
    Type: String
    Default: &amp;quot;lambda-packages/fibonacci/fibonacciPackage.zip&amp;quot;

Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: &#39;2012-10-17&#39;
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: &amp;quot;/&amp;quot;
      Policies:
      - PolicyName: root
        PolicyDocument:
          Version: &#39;2012-10-17&#39;
          Statement:
          - Effect: Allow
            Action:
            - logs:*
            Resource: arn:aws:logs:*:*:*

  FibonacciFunction: 
    Type: AWS::Lambda::Function
    Properties:
      Handler: main.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code: 
        S3Bucket: !Ref s3Bucket
        S3Key: !Ref s3Key
      Runtime: &#39;python3.8&#39;
      Timeout: 20
      MemorySize: 128
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;steps-required-for-python-lambda-packaging&#34;&gt;Steps required for python lambda packaging&lt;/h3&gt;
&lt;p&gt;Basically there are 2 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install your requirements locally&lt;/li&gt;
&lt;li&gt;zip it&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;install-requirements-in-the-local-directory&#34;&gt;install requirements in the local directory&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; makes this really easy, you can install multiple packages with a &lt;code&gt;requirements.txt&lt;/code&gt; file or a single one directly.&lt;/p&gt;
&lt;p&gt;Without requirements.txt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install boto3 -t ./
pip install requests -t ./
pip install git+https://github.com/psf/requests.git -t ./
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With requirements.txt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install -r requirements.txt -t ./
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you need python packages in your pipeline but don&amp;rsquo;t want to include them in you lambda package just use pip without the  &lt;code&gt;-t&lt;/code&gt; parameter. Also If you don&amp;rsquo;t like the amount of logs pip creates add a &lt;code&gt;--quiet&lt;/code&gt; parameter to silence the noisy output.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install --quiet awscli
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;zip-it&#34;&gt;zip it&lt;/h4&gt;
&lt;p&gt;Now zip your project, change the permissions to prevent issues surrounding that and exclude things you won&amp;rsquo;t need in runtime.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod -R 755 . # makes sure there are no permission errors 
zip -r lambdaPackage.zip . -x &amp;quot;.git*&amp;quot; --quiet
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;upload-to-s3&#34;&gt;Upload to S3&lt;/h3&gt;
&lt;p&gt;Deployment to lambda is easiest if you can point to your deployment zip in S3, to do this you could take a couple of naming roads I&amp;rsquo;ve used all of them, and technically there isn&amp;rsquo;t a clear best way to do it in my opinion.
You shouldn&amp;rsquo;t overwrite your old zip file when releasing a new version, this makes rollbacks only possible if you also rebuild your old version and that&amp;rsquo;s not the desired behavior.&lt;/p&gt;
&lt;p&gt;Here are a few of my favorite zip identifiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;git short hash &lt;code&gt;git rev-parse --short HEAD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;epoch date &lt;code&gt;date +%s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;random string &lt;code&gt;openssl rand -hex 12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;sem ver of the app &lt;code&gt;git tag --points-at HEAD&lt;/code&gt; (untested)&lt;/li&gt;
&lt;li&gt;build number &amp;gt; check your pipeline manual&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For now, I&amp;rsquo;ll use epoch timestamped zip files, but you could replace the &lt;code&gt;date +%s&lt;/code&gt; with whatever command you prefer to tag your lambda packages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zip -r lambdaPackage.zip . -x &amp;quot;.git*&amp;quot; --quiet
export zipname=&amp;quot;lambdaPackage-$(date +%s).zip&amp;quot;
aws s3 cp lambdaPackage.zip s3://bucket/lambda-packages/projectName/${zipname}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After those commands, you can use the env var &lt;code&gt;zipname&lt;/code&gt; in the following deployment to point to your new zip file.&lt;/p&gt;
&lt;h3 id=&#34;deploy-with-cloudformation&#34;&gt;Deploy with cloudformation&lt;/h3&gt;
&lt;p&gt;A deep dive into cloudformation is beyond the scope here so I&amp;rsquo;ll just give you my default basic deploy command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aws cloudformation deploy --template-file cfn.yml --capabilities CAPABILITY_IAM --stack-name fibonacci --no-fail-on-empty-changeset --parameter-overrides s3Bucket=&amp;quot;bucketname&amp;quot; s3Key=&amp;quot;lambda-packages/projectName/${zipname}&amp;quot; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you should be able to see your lambda in your AWS account, if not there are a couple of things that could have gone wrong. No panic at the bottom of this article I&amp;rsquo;ve listed some common problems you can have with this kind of setup and how to debug and fix them.&lt;/p&gt;
&lt;h3 id=&#34;testing-improving-and-finalizing-your-lambda-in-aws&#34;&gt;Testing, improving, and finalizing your lambda in aws&lt;/h3&gt;
&lt;p&gt;Now that your Lambda is ready to be used, have a go with the build-in testing in the web interface.
On the Lambda function page at the to you&amp;rsquo;ll find a bar with a test button next to it from the dropdown click &lt;code&gt;Configure test event&lt;/code&gt;, here you can input the json from your script or try out different examples. This test event in the web interface is my preferred way of trying new things in lambda because I have all the logs and metrics right in front of me.
If you&amp;rsquo;re looking for a more hands-off automated test system approach have a look at the 
&lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/reference/lambda/invoke.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;aws lambda invoke cli reference.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;From here, just repeat the following: tweak, commit, push, test, and once your results are to your likings connect it to your preferred trigger and start testing the whole stack. Keep in mind that triggers will need iam permissions to trigger your lambda.&lt;/p&gt;
&lt;h3 id=&#34;help-its-not-working-for-me&#34;&gt;Help it&amp;rsquo;s not working for me&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a checklist to fix most of the common issues with automated deployments like this one.&lt;/p&gt;
&lt;h4 id=&#34;aws-command-not-found&#34;&gt;aws command not found&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;pip install --quiet awscli&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;are-there-aws-credentials-attached-to-the-pipeline&#34;&gt;Are there AWS credentials attached to the pipeline?&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;aws sts get-caller-identity&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;deployment--upload-fails&#34;&gt;Deployment / upload fails&lt;/h4&gt;
&lt;p&gt;Do your iam credentials connected to the pipeline allow you to use: cloudformation, lambda, logs, and S3?
Did you select the correct bucket name and is that bucket already created, if not create it before you deploy.&lt;/p&gt;
&lt;h4 id=&#34;oserror-no-library-called--was-found&#34;&gt;OSError: no library called &amp;hellip; was found&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;[ERROR] OSError: no library called &amp;quot;cairo&amp;quot; was found no library called &amp;quot;libcairo-2&amp;quot; was found cannot load library &#39;libcairo.so&#39;: libexpat.so.1: cannot open shared object file: No such file or directory cannot load library &#39;libcairo.2.dylib&#39;: libcairo.2.dylib: cannot open shared object file: No such file or directory....
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&amp;rsquo;re trying to call an os lib that is not installed, you can fix this by building a lambda layer that provides the necessary lib files. A lambda layer is another zip file that provides some files to the container runtime a good place to start when building a lambda layer is the 
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;AWS Lambda layers&lt;/a&gt; page in the lambda configuration documentation. If you want to have a look at the solution for the layer solution to the above problem, check out 
&lt;a href=&#34;https://github.com/kotify/cloud-print-utils&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;cloud-print-utils.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;fibonacci-example-project-on-gitlabhttpsgitlabcomaapjeisbaasfibonacci-service&#34;&gt;
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/fibonacci-service&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Fibonacci example project on gitlab&lt;/a&gt;&lt;/h2&gt;
</description>
    </item>
    
    <item>
      <title>Popular Movies</title>
      <link>https://aapjeisbaas.nl/project/popular-movies/</link>
      <pubDate>Mon, 22 Jun 2020 15:03:34 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/project/popular-movies/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://hitcounter.pythonanywhere.com/count/tag.svg?url=https%3A%2F%2Fgitlab.com%2Faapjeisbaas%2Fpopular-movies&#34; alt=&#34;Hits&#34;&gt;&lt;/p&gt;
&lt;p&gt;An api to pull in top popular movies from imdb&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This api was inspired by Steven Lu&amp;rsquo;s 
&lt;a href=&#34;https://github.com/sjlu/popular-movies&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;popular-movies&lt;/a&gt; project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wanted to follow and filter the imdb 
&lt;a href=&#34;https://www.imdb.com/chart/moviemeter/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;moviemeter&lt;/a&gt; list but couldn&amp;rsquo;t find a direct api or a list from Steven Lu that reflected what I was looking for. So that&amp;rsquo;s where this project comes in.
It is written in python and runs in AWS as a lambda fuction triggerd by an api gateway which sits behind a cloudfront to cache the majority of the requests.
Within the function itself there&amp;rsquo;s also caching built in to prevent unnecessary calls to imdb and speed up responses to users if the data is already in memory.&lt;/p&gt;
&lt;p&gt;If you want to see the code behind this api have a look at the 
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/popular-movies&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;gitlab project page.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;options&#34;&gt;options&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query Param&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Effect&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;value ranges&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fresh&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Only return current and last year releases&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;true / false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;year&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Return $year and newer (disabled with fresh)&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;4 number year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rating&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Return $rating and higher&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;0 - 10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;votes&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Return movies with at least this many votes&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;1 - ∞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;max&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Max returned movies&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;1 - ∞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;list&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Select a imdb chart source default=popular&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;popular / top&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Example Queries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;max=20&amp;amp;rating=6&amp;amp;votes=50000&amp;amp;list=popular&#34;&gt;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;max=20&amp;amp;rating=6&amp;amp;votes=50000&amp;amp;list=popular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=top&#34;&gt;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=top&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;curl--jq&#34;&gt;curl | jq&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$ curl -s -X GET &#39;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;max=3&amp;amp;rating=6&amp;amp;votes=50000&amp;amp;list=popular&#39; |jq
[
  {
    &amp;quot;title&amp;quot;: &amp;quot;Extraction&amp;quot;,
    &amp;quot;imdb_id&amp;quot;: &amp;quot;tt8936646&amp;quot;,
    &amp;quot;rating&amp;quot;: 6.8,
    &amp;quot;year&amp;quot;: 2020,
    &amp;quot;votes&amp;quot;: 101076,
    &amp;quot;popular movies 100 rank&amp;quot;: 1
  },
  {
    &amp;quot;title&amp;quot;: &amp;quot;Star Wars: Episode IX - The Rise of Skywalker&amp;quot;,
    &amp;quot;imdb_id&amp;quot;: &amp;quot;tt2527338&amp;quot;,
    &amp;quot;rating&amp;quot;: 6.7,
    &amp;quot;year&amp;quot;: 2019,
    &amp;quot;votes&amp;quot;: 319690,
    &amp;quot;popular movies 100 rank&amp;quot;: 2
  },
  {
    &amp;quot;title&amp;quot;: &amp;quot;Once Upon a Time... in Hollywood&amp;quot;,
    &amp;quot;imdb_id&amp;quot;: &amp;quot;tt7131622&amp;quot;,
    &amp;quot;rating&amp;quot;: 7.7,
    &amp;quot;year&amp;quot;: 2019,
    &amp;quot;votes&amp;quot;: 462778,
    &amp;quot;popular movies 100 rank&amp;quot;: 6
  }
]

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;charts-you-can-query&#34;&gt;Charts you can query&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;list param&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;IMDb link&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;api example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;top&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;a href=&#34;https://www.imdb.com/chart/top/&#34;&gt;https://www.imdb.com/chart/top/&lt;/a&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;a href=&#34;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=top&#34;&gt;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=top&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;popular&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;a href=&#34;https://www.imdb.com/chart/moviemeter/&#34;&gt;https://www.imdb.com/chart/moviemeter/&lt;/a&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;a href=&#34;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=popular&#34;&gt;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;list=popular&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;radarr&#34;&gt;Radarr&lt;/h2&gt;
&lt;p&gt;This list by itself isn&amp;rsquo;t all that exiting, the magic happens when you use it in Radarr as a list source. This api uses a structure that is compatible with the 
&lt;a href=&#34;https://github.com/Radarr/Radarr/wiki/Supported-NetImports#stevenluimport&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;StevenLuImport&lt;/a&gt; so you can just input the query in the URL field.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please use at a sensible rate of max 1 query per hour in your radar setup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There seems to be an issue with the auto library clean future and StevenLu lists, so if you use this list you need to set: &lt;strong&gt;Clean Library Level&lt;/strong&gt; to: &lt;strong&gt;Log only&lt;/strong&gt;
There are already multiple github issues open to solve this, unfortunatley this is not in the regular releases yet. (In the aphrodite branch this is fixed, but trust me it is unstable and you don&amp;rsquo;t want to run it.)

&lt;a href=&#34;https://github.com/Radarr/Radarr/issues/4014&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;issue 4014&lt;/a&gt;

&lt;a href=&#34;https://github.com/Radarr/Radarr/pull/4016&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pull req&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-api-in-action&#34;&gt;The api in action&lt;/h2&gt;
&lt;p&gt;This table is created using the following url:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;amp;max=20&amp;amp;rating=6&amp;amp;votes=50000&lt;/code&gt;&lt;/p&gt;
&lt;table
  id=&#34;table&#34;
  data-url=&#34;https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&amp;max=20&amp;rating=6&amp;votes=50000&#34;
  data-toggle=&#34;table&#34;
  class=&#34;table&#34;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th data-field=&#34;title&#34;&gt;Movie Title&lt;/th&gt;
        &lt;th data-field=&#34;rating&#34;&gt;Rating&lt;/th&gt;
        &lt;th data-field=&#34;year&#34;&gt;Year&lt;/th&gt;
        &lt;th data-field=&#34;votes&#34;&gt;Votes&lt;/th&gt;
        &lt;th data-field=&#34;imdb_id&#34; data-formatter=&#34;operateFormatter&#34;&gt;IMDb&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
&lt;/table&gt;
&lt;h2 id=&#34;donate&#34;&gt;Donate&lt;/h2&gt;
&lt;p&gt;If you like this api and want to give something back please donate to keep it around and up to date.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;method&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;XRP&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;rPThf2QWMKKkmQM8hw9yD4bwpbmLYiz6vB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;ETH&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;0xe589531E35fA84d9d561f028070663004aca5Fa4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;BCH&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;bitcoincash:qr5t82vekd7ekx7hsfkdqlwsws67nq3dcg6hn92u8x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;BTC&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;15aaSnoGBUMxyGwt7JveSNT75hBCL14wu1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;iDeal&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;a href=&#34;https://bunq.me/svb/_/movies-api&#34;&gt;https://bunq.me/svb/_/movies-api&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;script src=&#34;https://code.jquery.com/jquery-3.3.1.min.js&#34; integrity=&#34;sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=&#34; crossorigin=&#34;anonymous&#34;&gt;&lt;/script&gt;
&lt;script src=&#34;https://unpkg.com/bootstrap-table@1.16.0/dist/bootstrap-table.min.js&#34;&gt;&lt;/script&gt;
&lt;script&gt;
  var $table = $(&#39;#table&#39;)

  function operateFormatter(value, row, index) {
    return [
      &#39;&lt;a class=&#34;imdb&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34; href=&#34;&#39;,
      &#39;https://www.imdb.com/title/&#39;,
      row[&#39;imdb_id&#39;],
      &#39;&#34; title=&#34;IMDb&#34;&gt;&#39;,
      &#39;&lt;img src=&#34;imdb.png&#34;&gt;&#39;,
      &#39;&lt;/a&gt;&#39;
    ].join(&#39;&#39;)
  }
&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>Rocksmith on Linux</title>
      <link>https://aapjeisbaas.nl/post/rocksmith-on-linux/</link>
      <pubDate>Tue, 10 Mar 2020 23:52:17 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/rocksmith-on-linux/</guid>
      <description>&lt;p&gt;Rocksmith is a game / guitar teacher for Windows and game consoles that allows you to use a real guitar to play minigames, learn actual songs and overall improve your guitar skills.
I&amp;rsquo;ve been playing this off and on since the original game out in late 2012 with 1 big frustration aside from the playing guitar is hard part.
It never had support for linux and when I got it to work I had problems with stability and a terribly complex setup.&lt;/p&gt;
&lt;p&gt;I have no latency issues because I don&amp;rsquo;t try to use my PC as a software amp like guitarix and have the audio output in the game turned off.
This doesn&amp;rsquo;t change the gameplay at all, I play with my headphone on one ear so I can hear my guitar with the other.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s fix it!&lt;/p&gt;
&lt;p&gt;To replicate my setup you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Electric guitar&lt;/li&gt;
&lt;li&gt;Real tone cable or any dedicated USB guitar line in device&lt;/li&gt;
&lt;li&gt;Linux desktop setup, (Arch / Fedora / Ubuntu)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;install-steam-and-rocksmith&#34;&gt;Install Steam and Rocksmith&lt;/h2&gt;
&lt;p&gt;I used flatpak and it is super stable for me if you dont have flatpak installed follow your distro&amp;rsquo;s install guide 
&lt;a href=&#34;https://flatpak.org/setup/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flatpak install flathub com.valvesoftware.Steam
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After the installation is complete go in to the setting and enable &lt;strong&gt;Steam Play&lt;/strong&gt; for all titles. (I tested with proton 3.16-9 but newer versions will probably work fine)&lt;/p&gt;
&lt;p&gt;Now go ahead and install 
&lt;a href=&#34;https://store.steampowered.com/app/221680&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Rocksmith&lt;/a&gt; and see if your guitar works instantly, if so have fun.&lt;/p&gt;
&lt;h2 id=&#34;fix-audio-input&#34;&gt;Fix audio input&lt;/h2&gt;
&lt;p&gt;Darn it no luck, but don&amp;rsquo;t worry we&amp;rsquo;ll get there.&lt;/p&gt;
&lt;p&gt;I created a &lt;code&gt;/usr/local/bin/steam&lt;/code&gt; with a disable pulse for wine, better home mapping and auto update on startup.
You will get this to work with only the export and the flatpak run but this is just how I like to start my steam client.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export WINENOPULSE=1
sudo flatpak update com.valvesoftware.Steam -y
sudo flatpak override com.valvesoftware.Steam --filesystem=$HOME
flatpak run com.valvesoftware.Steam
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find your steam library path mine is in &lt;code&gt;/home/svanbroekhoven/steam_lib/&lt;/code&gt; so replace that with your path in the examples below.&lt;/p&gt;
&lt;p&gt;To edit the wine config / registry files I used 
&lt;a href=&#34;https://github.com/Matoking/protontricks&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;protontricks&lt;/a&gt; and this can also be installed using a 
&lt;a href=&#34;https://github.com/flathub/com.valvesoftware.Steam.Utility.protontricks&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;flatpak&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[svanbroekhoven@alan ~]$ flatpak install flathub com.valvesoftware.Steam.Utility.protontricks
Looking for matches…


        ID                                             Branch    Op    Remote     Download
 1. [✓] com.valvesoftware.Steam.Utility.protontricks   stable    i     flathub    2.7 MB / 3.2 MB

Installation complete.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now check if your protontricks can find the installed games:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[svanbroekhoven@alan ~]$ alias protontricks-flat=&#39;flatpak run --command=protontricks com.valvesoftware.Steam --no-runtime&#39;
[svanbroekhoven@alan ~]$ protontricks-flat -s rocksmith
Found the following games:
Rocksmith® 2014 Edition - Remastered (221680)

To run protontricks for the chosen game, run:
$ protontricks APPID COMMAND

NOTE: A game must be launched at least once before protontricks can find the game.
[svanbroekhoven@alan ~]$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;rsquo;s change the sound system to alsa for this game:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[svanbroekhoven@alan ~]$ protontricks-flat 221680 sound=alsa
------------------------------------------------------
Your version of wine 3.16 is no longer supported upstream. You should upgrade to 4.x
------------------------------------------------------
------------------------------------------------------
You are using a 64-bit WINEPREFIX. Note that many verbs only install 32-bit versions of packages. If you encounter problems, please retest in a clean 32-bit WINEPREFIX before reporting a bug.
------------------------------------------------------
Using winetricks 20191224 - sha256sum: afe039a7d72553cb761f0367f9f2085b92af8caf86e025c34bbc1fdd89a1f9ee with wine-3.16 and WINEARCH=win64
Executing w_do_call sound=alsa
------------------------------------------------------
You are using a 64-bit WINEPREFIX. Note that many verbs only install 32-bit versions of packages. If you encounter problems, please retest in a clean 32-bit WINEPREFIX before reporting a bug.
------------------------------------------------------
Executing load_sound alsa
Setting sound driver to alsa
Executing /home/svanbroekhoven/steam_lib/steamapps/common/Proton 3.16/dist/bin/wine regedit C:\windows\Temp\set-sound.reg
wine: cannot find L&amp;quot;C:\\windows\\system32\\winemenubuilder.exe&amp;quot;
000b:err:wineboot:ProcessRunKeys Error running cmd L&amp;quot;C:\\windows\\system32\\winemenubuilder.exe -a -r&amp;quot; (2)
0021:err:module:load_builtin_dll failed to load .so lib for builtin L&amp;quot;winebus.sys&amp;quot;: libudev.so.0: cannot open shared object file: No such file or directory
0021:err:ntoskrnl:ZwLoadDriver failed to create driver L&amp;quot;\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WineBus&amp;quot;: c0000142
000f:fixme:service:scmdatabase_autostart_services Auto-start service L&amp;quot;WineBus&amp;quot; failed to start: 1114
0014:err:service:process_send_command service protocol error - failed to write pipe!
Executing /home/svanbroekhoven/steam_lib/steamapps/common/Proton 3.16/dist/bin/wine64 regedit C:\windows\Temp\set-sound.reg
wine: cannot find L&amp;quot;C:\\windows\\system32\\winemenubuilder.exe&amp;quot;
000b:err:wineboot:ProcessRunKeys Error running cmd L&amp;quot;C:\\windows\\system32\\winemenubuilder.exe -a -r&amp;quot; (2)
0021:err:module:load_builtin_dll failed to load .so lib for builtin L&amp;quot;winebus.sys&amp;quot;: libudev.so.0: cannot open shared object file: No such file or directory
0021:err:ntoskrnl:ZwLoadDriver failed to create driver L&amp;quot;\\Registry\\Machine\\System\\CurrentControlSet\\Services\\WineBus&amp;quot;: c0000142
0014:err:service:process_send_command service protocol error - failed to write pipe!
000f:fixme:service:scmdatabase_autostart_services Auto-start service L&amp;quot;WineBus&amp;quot; failed to start: 1114
[svanbroekhoven@alan ~]$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This output looks kinda broken but it works for me so&amp;hellip;.&lt;/p&gt;
&lt;p&gt;The last command edited our .reg files in /home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx/
and the most notable change is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx/system.reg | grep &#39;Pulse&#39; -B 5 -A 5  
#time=1d5f7264ac701fc
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture\\{25DA76D0-033C-4235-9002-19F48894AC6F}\\Properties] 1583876540
#time=1d5f724c70e68ec
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000004
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{25DA76D0-033C-4235-9002-19F48894AC6F}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
--
#time=1d5f7264ac7137c
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render\\{FD47D9CC-4218-4135-9CE2-0C195C87405B}\\Properties] 1583876540
#time=1d5f724c70e5848
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000001
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},3&amp;quot;=dword:00000003
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{FD47D9CC-4218-4135-9CE2-0C195C87405B}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now for out final step use winecfg to manualy set the mic and line input to our sound input card.
For that you first select the correct wineprefix.&lt;/p&gt;
&lt;p&gt;You might need to install wine on your system yo be able to run winecfg.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo pacman -S wine&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# deafult flatpak location
export WINEPREFIX=&amp;quot;/home/svanbroekhoven/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/compatdata/221680/pfx&amp;quot;
# custom steam library location
export WINEPREFIX=&amp;quot;/home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx&amp;quot;
winecfg
0029:err:setupapi:create_dest_file failed to create L&amp;quot;C:\\windows\\system32\\atl100.dll&amp;quot; (error=80)
0029:err:winediag:SECUR32_initNTLMSP ntlm_auth was not found or is outdated. Make sure that ntlm_auth &amp;gt;= 3.0.25 is in your path. Usually, you can find it in the winbind package of your distribution.
0029:fixme:dwmapi:DwmIsCompositionEnabled 000000006DFA0434
002e:fixme:iphlpapi:NotifyIpInterfaceChange (family 0, callback 0x69ed306d, context 0x421060, init_notify 0, handle 0x182fa00): stub
0029:fixme:ntdll:NtQuerySystemInformation info_class SYSTEM_PERFORMANCE_INFORMATION
0040:err:setupapi:create_dest_file failed to create L&amp;quot;C:\\windows\\system32\\atl100.dll&amp;quot; (error=80)
0040:err:winediag:gnutls_initialize failed to load libgnutls, no support for encryption
0040:err:winediag:gnutls_initialize failed to load libgnutls, no support for pfx import/export
0040:err:module:load_so_dll failed to load .so lib &amp;quot;/usr/bin/../lib32/wine/mp3dmod.dll.so&amp;quot;: libmpg123.so.0: cannot open shared object file: No such file or directory
0040:err:winediag:schan_imp_init Failed to load libgnutls, secure connections will not be available.
0040:err:winediag:SECUR32_initNTLMSP ntlm_auth was not found or is outdated. Make sure that ntlm_auth &amp;gt;= 3.0.25 is in your path. Usually, you can find it in the winbind package of your distribution.
0040:err:winediag:load_gssapi_krb5 Failed to load libgssapi_krb5, Kerberos SSP support will not be available.
0040:fixme:dwmapi:DwmIsCompositionEnabled 6D95DD14
0042:fixme:iphlpapi:NotifyIpInterfaceChange (family 0, callback 0x6a0df537, context 0x3af348, init_notify 0, handle 0x176fce8): stub
0040:fixme:ntdll:NtQuerySystemInformation info_class SYSTEM_PERFORMANCE_INFORMATION
0040:err:module:load_so_dll failed to load .so lib &amp;quot;/usr/bin/../lib32/wine/winegstreamer.dll.so&amp;quot;: libgstvideo-1.0.so.0: cannot open shared object file: No such file or directory
wine: configuration in L&amp;quot;/home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx&amp;quot; has been updated.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accept all the install crap from wine&lt;/p&gt;
&lt;p&gt;wait&amp;hellip;&lt;/p&gt;
&lt;p&gt;Go to the Audio tab, select Rocksmith cable for input and apply the changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;winecfg.jpg&#34; alt=&#34;winecfg.jpg&#34;&gt;&lt;/p&gt;
&lt;p&gt;Close the winecfg app and start your game.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s still not perfect have a look at &lt;code&gt;/home/svanbroekhoven/steam_lib/steamapps/common/Rocksmith2014/Rocksmith.ini&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;[Audio]
EnableMicrophone=1
ExclusiveMode=0
LatencyBuffer=4
ForceDefaultPlaybackDevice=
ForceWDM=0
ForceDirectXSink=0
DumpAudioLog=0
MaxOutputBufferSize=0
RealToneCableOnly=0
Win32UltraLowLatencyMode=0
[Renderer.Win32]
ShowGamepadUI=0
ScreenWidth=0
ScreenHeight=0
Fullscreen=1
VisualQuality=2
RenderingWidth=0
RenderingHeight=0
EnablePostEffects=1
EnableShadows=1
EnableHighResScope=1
EnableDepthOfField=1
EnablePerPixelLighting=1
MsaaSamples=4
DisableBrowser=0
[Net]
UseProxy=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just to be as complete as possible here are some snippets from the system.reg&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[svanbroekhoven@alan ~]$ cat /home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx/system.reg | grep &#39;Pulse&#39; -B 5 -A 5
#time=1d5f7264ac701fc
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture\\{25DA76D0-033C-4235-9002-19F48894AC6F}\\Properties] 1583876540
#time=1d5f724c70e68ec
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000004
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{25DA76D0-033C-4235-9002-19F48894AC6F}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
--
#time=1d5f7264ac7137c
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render\\{FD47D9CC-4218-4135-9CE2-0C195C87405B}\\Properties] 1583876540
#time=1d5f724c70e5848
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000001
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},3&amp;quot;=dword:00000003
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{FD47D9CC-4218-4135-9CE2-0C195C87405B}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
[svanbroekhoven@alan ~]$ cat /home/svanbroekhoven/steam_lib/steamapps/compatdata/221680/pfx/system.reg | grep &#39;Pulse\|Rock&#39; -B 5 -A 5
#time=1d5f7264ac701fc
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture\\{25DA76D0-033C-4235-9002-19F48894AC6F}\\Properties] 1583876540
#time=1d5f724c70e68ec
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000004
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{25DA76D0-033C-4235-9002-19F48894AC6F}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,01,00,44,ac,00,00,10,b1,\
  02,00,04,00,20,00,16,00,20,00,04,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
--
#time=1d5f73a4b70c2be
&amp;quot;DeviceState&amp;quot;=dword:00000001

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture\\{3DDB3262-1A9F-4279-A79D-7E1742D69ED2}\\Properties] 1583877781
#time=1d5f727aa5675de
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;In: Rocksmith USB Guitar Adapter - USB Audio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000004
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{3DDB3262-1A9F-4279-A79D-7E1742D69ED2}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;In: Rocksmith USB Guitar Adapter - USB Audio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;In: Rocksmith USB Guitar Adapter - USB Audio&amp;quot;
&amp;quot;{B3F8FA53-0004-438E-9003-51A46E139BFC},2&amp;quot;=&amp;quot;{1}.USB\\VID_12BA&amp;amp;PID_00FF\\0&amp;amp;42D69ED2&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,02,00,80,bb,00,00,00,dc,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,02,00,80,bb,00,00,00,dc,\
--
#time=1d5f7264ac7137c
&amp;quot;DeviceState&amp;quot;=dword:00000004

[Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render\\{FD47D9CC-4218-4135-9CE2-0C195C87405B}\\Properties] 1583876540
#time=1d5f724c70e5848
&amp;quot;{026E516E-B814-414B-83CD-856D6FEF4822},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},0&amp;quot;=dword:00000001
&amp;quot;{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F0E},3&amp;quot;=dword:00000003
&amp;quot;{233164C8-1B2C-4C7D-BC68-B671687A2567},1&amp;quot;=&amp;quot;{FD47D9CC-4218-4135-9CE2-0C195C87405B}&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},14&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{A45C254E-DF1C-4EFD-8020-67D146A850E0},2&amp;quot;=&amp;quot;Pulseaudio&amp;quot;
&amp;quot;{E4870E26-3CC5-4CD2-BA46-CA0A9A70ED04},3&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
  aa,00,38,9b,71
&amp;quot;{F19F064D-082C-4E27-BC73-6882A1BB8E4C},0&amp;quot;=hex:fe,ff,02,00,44,ac,00,00,20,62,\
  05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,\
--
&amp;quot;SymbolicLinkValue&amp;quot;=hex(6):5c,00,52,00,45,00,47,00,49,00,53,00,54,00,52,00,59,\
  00,5c,00,4d,00,41,00,43,00,48,00,49,00,4e,00,45,00,5c,00,53,00,6f,00,66,00,\
  74,00,77,00,61,00,72,00,65,00,5c,00,57,00,69,00,6e,00,65,00,5c,00,50,00,6f,\
  00,72,00,74,00,73,00

[Software\\Wow6432Node\\Wow6432Node\\Ubisoft\\Rocksmith2014] 1583876436
#time=1d5f72489298714
&amp;quot;ExecutableName&amp;quot;=&amp;quot;Rocksmith2014.exe&amp;quot;
&amp;quot;installdir&amp;quot;=&amp;quot;Z:\\home\\svanbroekhoven\\steam_lib\\steamapps\\common\\Rocksmith2014&amp;quot;

[System\\CurrentControlSet\\Control\\Class\\{4d36e967-e325-11ce-bfc1-08002be10318}] 1554732615
#time=1d4ee14ca089e24
@=&amp;quot;Disk drives&amp;quot;
&amp;quot;Class&amp;quot;=&amp;quot;DiskDrive&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>NS International move to Aws</title>
      <link>https://aapjeisbaas.nl/project/nsinternational-migration-to-aws/</link>
      <pubDate>Sat, 07 Mar 2020 16:09:42 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/project/nsinternational-migration-to-aws/</guid>
      <description>&lt;p&gt;The term lift and shift was thrown out the window and we’ve build a fully automated instructure build pipeline to facilitate the apps. From old jboss to modern docker based apps.&lt;/p&gt;
&lt;p&gt;NS International, formerly NS Hispeed, is the rail operator in the Netherlands that operates international intercity and high-speed train connections with Belgium, France, Germany and Switzerland. NS International is part of the Dutch Railways. It currently operates both direct and indirect services. The largest online service they provide is ticket sales for international train journeys.
From my role as an architect / sr. cloud engineer I am responsible for the design and delivery of the Cloud infrastructure platform based on AWS, in collaboration with the rest of our SRE team, which serves as an underlay for the digital ticket office.
This role has been very diverse and has changed a lot over the years, when we started the transition to AWS, there was no cloud native plan to make the move possible, but only a list of two applications that eventually had to be moved to the aws platform. We were able to place these 2 applications (tomcat web frontend, jboss api backend) on AWS by creating an automatic AMI building process that uses ansible for the base configutation and prepares them for retrieving the ear / war application when starting up from S3.
The platform has changed considerably twice, first with docker / ecs on an auto scaling ec2 cluster (I already designed this in 2016 and AWS now offers almost the same features in 2020) and finally ecs with fargate as a compute layer. The 16-year-old jboss app has also gone to ecs fargate with a lot of help from our developers, this was one of the jigsaw pieces to get this environment PCI DSS Compliant and we now officially have the stamp of PCI for our environment. The platform now has around 50 application stacks in production consisting of: spring boot, jboss, tomcat, s3 proxy, node and python.&lt;/p&gt;
&lt;p&gt;Within this assignment I applied the following techniques:
Cloudformation, Python, Ansible, bash, gitlab, azure devops, jira, docker, clair, server spec&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;nederlands&#34;&gt;Nederlands&lt;/h3&gt;
&lt;p&gt;NS International, voorheen NS Hispeed, is de spoor exploitant in Nederland die internationale intercity- en hogesnelheidstrein verbindingen met België, Frankrijk, Duitsland en Zwitserland exploiteert. NS International maakt deel uit van de Nederlandse Spoorwegen. Het exploiteert momenteel zowel snelle als niet-snelle diensten. De grootste online dienst die zij leveren is de kaartverkoop voor internationale treinreizen.
Vanuit mijn rol als architect/sr. cloud engineer ben ik in samenwerking met het de rest van ons SRE team verantwoordelijk voor het design en de oplevering  van het Cloud infra platform gebaseerd op AWS welke als onderlaag dienst voor het digitale loket voor de kaartverkoop.
Deze rol is erg divers en veel veranderd door de jaren heen, toen we begonnen aan de transitie naar AWS was er nog geen cloud native plan om de verhuizing mogelijk te maken  maar alleen een lijstje van twee  applicaties die uiteindelijk verhuisd moesten worden naar het aws platform. We hebben deze 2 applicaties (tomcat web frontend, jboss api backend) op AWS kunnen neerzetten door een automatisch ami bouwprocess te maken die met ansible servers opbouwt en klaar zet voor het ophalen van de applicatie ear/war bij het opstarten uit S3.
Het platform is inmiddels 2 keer behoorlijk veranderd, eerst met docker/ecs op een auto scaling ec2 cluster (ik heb dit 2016 al een keer ontworpen en AWS biedt bijna dezelfde functies nu net aan in 2020) en uiteindelijk ecs met fargate als compute laag. De jboss app van inmiddels 16 jaar oud is door ons team en hulp van developers ook naar ecs fargate gegaan, dit was een van de puzzel stukjes om deze omgeving PCI DSS Compliant te krijgen en inmiddels hebben we officieel de stempel van PCI voor onze omgeving. Het platform heeft inmiddels ongeveer 50 applicatie stacks in productie bestaande uit: springboot, jboss, tomcat, s3-proxy, node, python&lt;/p&gt;
&lt;p&gt;Binnen deze opdracht heb ik de volgende technieken toegepast:
Cloudformation, Python, Ansible, bash, gitlab, azure devops, jira, docker, clair, serverspec&lt;/p&gt;
&lt;p&gt;AWS services:
ECS, VPC, EC2, Fargate, RDS (aurora serverless/classic), Elasticache, ALBv2, Route53, Organization, S3, Cloudfront, IAM, SSM, Cloudwatch, ApplicationAutoScaling, SQS, EFS, CertificateManager, Lambda, ApiGateway, DynamoDB, ElasticBeanstalk, WAF&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>CPload</title>
      <link>https://aapjeisbaas.nl/slides/cpload/</link>
      <pubDate>Thu, 05 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://aapjeisbaas.nl/slides/cpload/</guid>
      <description>&lt;h2 id=&#34;testing-with-cpload&#34;&gt;Testing with CPload&lt;/h2&gt;
&lt;script id=&#34;asciicast-305069&#34; src=&#34;https://asciinema.org/a/305069.js&#34; async data-autoplay=&#34;true&#34; data-loop=&#34;true&#34;&gt;&lt;/script&gt;
&lt;p&gt;&lt;span class=&#34;fragment &#34; &gt;
Stein van Broekhoven - Cloud People
&lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;why-even-build-this-tool&#34;&gt;Why even build this tool?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Other tools were:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not easy enough&lt;/li&gt;
&lt;li&gt;bloated with too many options I didn&amp;rsquo;t need&lt;/li&gt;
&lt;li&gt;not easy to hack on (by me)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;features-i-was-looking-for&#34;&gt;Features I was looking for&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;use access logs for traffic generation&lt;/li&gt;
&lt;li&gt;change fqdn to be able to hit specific endpoints&lt;/li&gt;
&lt;li&gt;gradual increase of traffic&lt;/li&gt;
&lt;li&gt;rewrite specific requests&lt;/li&gt;
&lt;li&gt;custom pre script (build session)&lt;/li&gt;
&lt;li&gt;respect cookies / sessions / tokens etc.&lt;/li&gt;
&lt;li&gt;high throughput per cpu core #lean&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;use-cases&#34;&gt;Use cases&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;During platform migration&lt;/li&gt;
&lt;li&gt;In your application testing suite&lt;/li&gt;
&lt;li&gt;Rebuilding or splitting applications&lt;/li&gt;
&lt;li&gt;Testing new infra components&lt;/li&gt;
&lt;li&gt;Testing auto scaling&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&#34;notes&#34;&gt;
  &lt;ul&gt;
&lt;li&gt;During platform migration to validate stability before switchover.&lt;/li&gt;
&lt;li&gt;In your application testing suite, to validate efficiency.&lt;/li&gt;
&lt;li&gt;While rebuilding or splitting applications smaller components check if performance isn&amp;rsquo;t degraded.&lt;/li&gt;
&lt;li&gt;When evaluating any new infra components, use it to compare performance/price to the old situation.&lt;/li&gt;
&lt;li&gt;Testing auto scaling points, check if you are early enough with your scale up.&lt;/li&gt;
&lt;/ul&gt;

&lt;/aside&gt;
&lt;hr&gt;
&lt;h3 id=&#34;setup&#34;&gt;Setup&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git clone https://gitlab.com/cloud-people/cpload.git
cd cpload
mkvenv || pip install --user -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;script id=&#34;asciicast-313932&#34; src=&#34;https://asciinema.org/a/313932.js&#34; async data-autoplay=&#34;true&#34; data-size=&#34;8px&#34;&gt;&lt;/script&gt;
&lt;hr&gt;
&lt;h3 id=&#34;filters&#34;&gt;Filters&lt;/h3&gt;
&lt;p&gt;They&amp;rsquo;re defined in a python file called &lt;code&gt;filters.py&lt;/code&gt; you need 2 steps to add a filter&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Write a function that expands this basic one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def uri_editor(http_pool, url):
    send_request(http_pool, url)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the function to the filter list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;filters = {
    &#39;/magic/&#39;: magic_edit,
    &#39;api/v1/date&#39;: future,
    &#39;web&#39;: to_upper,
    &#39;&#39;: send_request
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&#34;filter-example&#34;&gt;Filter example&lt;/h3&gt;
&lt;p&gt;Turn all urls containing &lt;em&gt;&lt;code&gt;api&lt;/code&gt;&lt;/em&gt; in to &lt;em&gt;ALL CAPS&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;# ALL CAPS FOR ADDED DRAMA
def to_upper(http_pool, url):
    &amp;quot;&amp;quot;&amp;quot;URI TO UPPER&amp;quot;&amp;quot;&amp;quot;
    send_request(http_pool, str(url).upper())

filters = {
    &#39;api&#39;: to_upper,
    &#39;&#39;: send_request
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;input=&#39;/api/v2/lalala/happy/url?data=test&#39;
request=&#39;/API/V2/LALALA/HAPPY/URL?DATA=TEST&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&#34;what-to-feed-this-tool&#34;&gt;What to feed this tool&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Apache access logs&lt;/li&gt;
&lt;li&gt;Nginx access logs&lt;/li&gt;
&lt;li&gt;AWS ALB access logs&lt;/li&gt;
&lt;li&gt;Hand crafted url list&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;a-look-at-the-options&#34;&gt;A look at the options&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-txt&#34;&gt;
optional arguments:
  -h, --help           show this help message and exit
  --urlfile URLFILE    the cleaned up http log
  --fromuri FROMURI    base uri/hostname as in the log file, this is used to edit it to the destination uri.
  --touri TOURI        the uri/hostname you want to send the traffic to
  --ratemin RATEMIN    minimum req/hour
  --ratemax RATEMAX    maximum req/hour
  --ramptime RAMPTIME  ramp up time in hours
  --duration DURATION  total test duration in hours
  --verbose            Output all request results
  --filters FILTERS    the uri filters and rewrites file

&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&#34;now-lets-fire-it-up&#34;&gt;Now let&amp;rsquo;s fire it up&lt;/h2&gt;
</description>
    </item>
    
    <item>
      <title>Load test with CPload</title>
      <link>https://aapjeisbaas.nl/post/load-test-with-cpload/</link>
      <pubDate>Tue, 03 Mar 2020 16:32:54 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/load-test-with-cpload/</guid>
      <description>&lt;p&gt;When building large complex infrastructures it becomes harder to validate if the performance you need can be provided by the system you&amp;rsquo;ve build.
But customer demand for ever faster websites is growing by the day, how do you make sure you&amp;rsquo;ll be able to handle the next big sale or event on your web platform?
I use a load generator that can replay access logs and has a way to slowly ramp up traffic to replicate a gradual or sudden inrush of traffic.
Aside from the basics there are often uris you&amp;rsquo;d like to hit with a bit more precision than just replaying logs.
That is where the 
&lt;a href=&#34;https://gitlab.com/cloud-people/cpload/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CPload&lt;/a&gt; tool comes in, it has a python config file were you can run custom python code/http requests when specific uris pass through the log.&lt;/p&gt;
&lt;h3 id=&#34;some-of-the-cases-where-you-might-want-custom-http-requests-in-your-load-test&#34;&gt;Some of the cases where you might want custom http requests in your load test&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Steps in an order process where your end user has session data attached to their requests.&lt;/li&gt;
&lt;li&gt;A date that only makes sense to be in the future could be automatically replaced with a date in the future.&lt;/li&gt;
&lt;li&gt;If there is a certain set of backends you don&amp;rsquo;t want to hit in your test so you might want to exclude them or alter the requests that would normally hit this backend.&lt;/li&gt;
&lt;/ul&gt;
&lt;script id=&#34;asciicast-305069&#34; src=&#34;https://asciinema.org/a/305069.js&#34; async data-autoplay=&#34;true&#34; data-loop=&#34;true&#34;&gt;&lt;/script&gt;
&lt;h3 id=&#34;use-cases&#34;&gt;Use cases&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;During platform migration to validate stability before switchover.&lt;/li&gt;
&lt;li&gt;In the application testing suite, to validate efficiency.&lt;/li&gt;
&lt;li&gt;While rebuilding or splitting applications smaller components check if performance isn&amp;rsquo;t degraded.&lt;/li&gt;
&lt;li&gt;When evaluating any new infra components, use it to compare performance/price to the old situation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use it mainly for testing the auto scaling configuration to verify if the extra infrastructures spins up fast enough to handle the increase in traffic.
Also it is useful to have a super static traffic load and be able to see how many instances you need to handle that traffic, if this increases between releases have a look at where your code might be slowing things down and see if it is acceptable or should be tuned for more performance.&lt;/p&gt;
&lt;h3 id=&#34;why-yet-another-tool&#34;&gt;Why yet another tool&lt;/h3&gt;
&lt;p&gt;Other tools were not fixing my needs, were extremely expensive or had a way to broad feature set that I didn&amp;rsquo;t need.
I needed something that runs everywhere with not to much hassle and had the following requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;use access logs for traffic generation&lt;/li&gt;
&lt;li&gt;change the fqdn to be able to hit a specific endpoint&lt;/li&gt;
&lt;li&gt;gradual increase of the traffic&lt;/li&gt;
&lt;li&gt;rewrite specific requests&lt;/li&gt;
&lt;li&gt;custom pre script (build session)&lt;/li&gt;
&lt;li&gt;respect cookies / sessions / tokens etc.&lt;/li&gt;
&lt;li&gt;high throughput per cpu core #lean&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;examples&#34;&gt;Examples&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at some examples, first off a simple http 200 responder written in python.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Clone the git repo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ cd ~/git
$ git clone https://gitlab.com/cloud-people/cpload.git
$ cd cpload
$ tree
.
├── cpload.py
├── Dockerfile
├── examples
├── filters.py
├── LICENSE
├── logs.txt
├── README.md
└── requirements.txt

1 directory, 7 files
$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Get a http 200 responder container running&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ docker run -d -p 8080:8080 docker.io/aapjeisbaas/hello-container
9ed24540565844f6fc2de96f07c886caaf4cc358fd730936f718fbde56bc849e
$ curl 127.0.0.1:8080/kjkjkjkj
Your path is: /kjkjkjkj
$ 
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Install requirements and run our first test&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ pip install -r requirements
...
...
$ python cpload.py --verbose --touri &amp;quot;http://127.0.0.1:8080&amp;quot; --ratemin 360 --ratemax 7200 --ramptime 0.1 --duration 0.2
Load test running at: 360 req/hour
200 http://127.0.0.1:8080/
200 http://127.0.0.1:8080/web/location/
200 http://127.0.0.1:8080/api/v1/location/?test=true
200 http://127.0.0.1:8080/web/location/nested/pages
200 http://127.0.0.1:8080/api/v1/date-sensitive/20200213/test
Load test running at: 873 req/hour
200 http://127.0.0.1:8080/
200 http://127.0.0.1:8080/api/v1/add/controlled/randomness/magic/test
200 http://127.0.0.1:8080/
200 http://127.0.0.1:8080/
200 http://127.0.0.1:8080/web/location/
200 http://127.0.0.1:8080/api/v1/location/?test=true
^C
Stopping load test at: 1272 req/hour

 $ 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&amp;rsquo;s add the example url filters to edit some of the requests flowing through here.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python cpload.py --verbose --touri &amp;quot;http://127.0.0.1:8080&amp;quot; --ratemin 360 --ratemax 7200 --ramptime 0.1 --duration 0.2 --filters filters.py
loaded filters:

 function      help text                   uri filter
------------  --------------------------  ------------
magic_edit    Select only magic products  /magic/
future        Always use future dates     api/v1/date
to_upper      URI TO UPPER                web
send_request  Send the get request 

Load test running at: 360 req/hour
200 http://127.0.0.1:8080/
Load test running at: 379 req/hour
200 HTTP://127.0.0.1:8080/WEB/LOCATION/
200 http://127.0.0.1:8080/api/v1/location/?test=true
200 HTTP://127.0.0.1:8080/WEB/LOCATION/NESTED/PAGES
200 http://127.0.0.1:8080/api/v1/date-sensitive/20200317/test
Load test running at: 873 req/hour
200 http://127.0.0.1:8080/
200 http://127.0.0.1:8080/api/v1/add/controlled/randomness/magic1/test
200 http://127.0.0.1:8080/
^C
Stopping load test at: 1082 req/hour 

 $  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;the true power is in the filters&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As you can see in the output different requests were sent to the target but both were using the same source logs.txt file.
The url edits are done in the &lt;code&gt;filters.py&lt;/code&gt; file, have a look at some of the examples in there:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;print(&amp;quot;loaded filters:&amp;quot;)
# this file is included in the main cpload.py to add special sauce to your log replay.

# Here&#39;s the base function, what you do inside it is up to you.
#
# def uri_editor(http_pool, url):
#     send_request(http_pool, url)
#

# Here we select a random product from the magic list instead of using the one from the log.
# This might be helpfull when you have a small subset of the products in you tst / acc / mock environment
magicList = [&#39;magic1&#39;, &#39;product2&#39;, &#39;destination3&#39;, &#39;package4&#39;]
def magic_edit(http_pool, url):
    &amp;quot;&amp;quot;&amp;quot;Select only magic products&amp;quot;&amp;quot;&amp;quot;
    magic = random.choice(magicList)
    url = str(args.touri) + &amp;quot;/api/v1/add/controlled/randomness/&amp;quot; + magic + &amp;quot;/test&amp;quot;
    send_request(http_pool, url)

# In this example we replace a uri that is date sensitive with an url that always has a date in the future.
today = datetime.date.today()
def future(http_pool, url):
    &amp;quot;&amp;quot;&amp;quot;Always use future dates&amp;quot;&amp;quot;&amp;quot;
    days = datetime.timedelta(days=random.randint(3, 20))
    date = today + days
    url = str(args.touri) + &amp;quot;/api/v1/date-sensitive/&amp;quot; + date.strftime(&amp;quot;%Y%m%d&amp;quot;) + &amp;quot;/test&amp;quot;
    send_request(http_pool, url)

# ALL CAPS FOR ADDED DRAMA
def to_upper(http_pool, url):
    &amp;quot;&amp;quot;&amp;quot;URI TO UPPER&amp;quot;&amp;quot;&amp;quot;
    send_request(http_pool, str(url).upper())

# This is how the uri gets mapped to the correct editor function.
# It&#39;s selected based on if a string is string in uri, the first match will be used.
filters = {
    &#39;/magic/&#39;: magic_edit,
    &#39;api/v1/date&#39;: future,
    &#39;web&#39;: to_upper,
    &#39;&#39;: send_request
}

# draw a pretty overview of the loaded filters
from tabulate import tabulate
table = [[&amp;quot;function&amp;quot;, &amp;quot;help text&amp;quot;, &amp;quot;uri filter&amp;quot;]]
for filter in filters:
    table.append([filters[filter].__name__, filters[filter].__doc__, filter])
print(&amp;quot;\n&amp;quot;, tabulate(table,headers=&amp;quot;firstrow&amp;quot;), &amp;quot;\n&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;replay-your-own-logs&#34;&gt;Replay your own logs&lt;/h2&gt;
&lt;p&gt;To be able to replay your logs you need to clean them and make them look like the bellow example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;base.uri/
base.uri/web/location/
base.uri/api/v1/location/?test=true
base.uri/web/location/nested/pages
base.uri/api/v1/date-sensitive/20200213/test
base.uri/
base.uri/api/v1/add/controlled/randomness/magic/test
base.uri/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One liners to generate this from access logs.&lt;/p&gt;
&lt;h3 id=&#34;nginx&#34;&gt;nginx&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;10.0.2.2 - - [03/Mar/2020:14:28:05 +0000] &amp;quot;GET / HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:10 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/yolo&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /yolo HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:10 +0000] &amp;quot;GET /yolo HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:13 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/yolo/test&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /yolo/test HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:13 +0000] &amp;quot;GET /yolo/test HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:21 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/yolo/cloud-people&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /yolo/cloud-people HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:21 +0000] &amp;quot;GET /yolo/cloud-people HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:26 [error] 2#2: *1 &amp;quot;/usr/share/nginx/html/yolo/cloud/index.html&amp;quot; is not found (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /yolo/cloud/ HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:26 +0000] &amp;quot;GET /yolo/cloud/ HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:31 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/static&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /static HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:31 +0000] &amp;quot;GET /static HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:35 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/lalalala&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /lalalala HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:35 +0000] &amp;quot;GET /lalalala HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:28:41 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/robots.txt&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /robots.txt HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:41 +0000] &amp;quot;GET /robots.txt HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:54 +0000] &amp;quot;GET /?test=true HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:28:58 +0000] &amp;quot;GET /?test=fals HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:00 +0000] &amp;quot;GET /?test=false HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:08 +0000] &amp;quot;GET /?search=a HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:10 +0000] &amp;quot;GET /?search=ab HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:12 +0000] &amp;quot;GET /?search=abc HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:13 +0000] &amp;quot;GET /?search=abcd HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:15 +0000] &amp;quot;GET /?search=abcde HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:18 +0000] &amp;quot;GET /?search=abcdef HTTP/1.1&amp;quot; 200 612 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
2020/03/03 14:29:28 [error] 2#2: *1 open() &amp;quot;/usr/share/nginx/html/result/76353220&amp;quot; failed (2: No such file or directory), client: 10.0.2.2, server: localhost, request: &amp;quot;GET /result/76353220 HTTP/1.1&amp;quot;, host: &amp;quot;127.0.0.1:8080&amp;quot;
10.0.2.2 - - [03/Mar/2020:14:29:28 +0000] &amp;quot;GET /result/76353220 HTTP/1.1&amp;quot; 404 153 &amp;quot;-&amp;quot; &amp;quot;Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:72.0) Gecko/20100101 Firefox/72.0&amp;quot; &amp;quot;-&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;$ docker logs 39c50e2d31ee | grep -ve &#39;\[error\]&#39; | grep &#39;GET\|POST&#39; | awk &#39;{print &amp;quot;base.uri&amp;quot; $7}&#39;
base.uri/
base.uri/yolo
base.uri/yolo/test
base.uri/yolo/cloud-people
base.uri/yolo/cloud/
base.uri/static
base.uri/lalalala
base.uri/robots.txt
base.uri/?test=true
base.uri/?test=fals
base.uri/?test=false
base.uri/?search=a
base.uri/?search=ab
base.uri/?search=abc
base.uri/?search=abcd
base.uri/?search=abcde
base.uri/?search=abcdef
base.uri/result/76353220
$ docker logs 39c50e2d31ee | grep -ve &#39;\[error\]&#39; | grep &#39;GET\|POST&#39; | awk &#39;{print &amp;quot;base.uri&amp;quot; $7}&#39; &amp;gt; nginx-logs.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the test with the new log file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ python cpload.py --verbose --touri &amp;quot;http://127.0.0.1:8080&amp;quot; --ratemin 360 --ratemax 7200 --ramptime 0.1 --duration 0.2 --filters filters.py --urlfile nginx-logs.txt
loaded filters:

 function      help text                   uri filter
------------  --------------------------  ------------
magic_edit    Select only magic products  /magic/
future        Always use future dates     api/v1/date
to_upper      URI TO UPPER                web
send_request  Send the get request 

Load test running at: 360 req/hour
200 http://127.0.0.1:8080/
404 http://127.0.0.1:8080/yolo
404 http://127.0.0.1:8080/yolo/test
404 http://127.0.0.1:8080/yolo/cloud-people
404 http://127.0.0.1:8080/yolo/cloud/
Load test running at: 873 req/hour
404 http://127.0.0.1:8080/static
404 http://127.0.0.1:8080/lalalala
404 http://127.0.0.1:8080/robots.txt
Load test running at: 1082 req/hour
200 http://127.0.0.1:8080/?test=true
200 http://127.0.0.1:8080/?test=fals
200 http://127.0.0.1:8080/?test=false
200 http://127.0.0.1:8080/?search=a
200 http://127.0.0.1:8080/?search=ab
200 http://127.0.0.1:8080/?search=abc
200 http://127.0.0.1:8080/?search=abcd
Load test running at: 1462 req/hour
200 http://127.0.0.1:8080/?search=abcde
200 http://127.0.0.1:8080/?search=abcdef
404 http://127.0.0.1:8080/result/76353220
...
...
...

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;httpd--apache&#34;&gt;httpd / apache&lt;/h3&gt;
&lt;p&gt;Raw log:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AH00558: httpd: Could not reliably determine the server&#39;s fully qualified domain name, using 10.0.2.100. Set the &#39;ServerName&#39; directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server&#39;s fully qualified domain name, using 10.0.2.100. Set the &#39;ServerName&#39; directive globally to suppress this message
[Tue Mar 03 14:44:26.181291 2020] [mpm_event:notice] [pid 1:tid 140004244472960] AH00489: Apache/2.4.41 (Unix) configured -- resuming normal operations
[Tue Mar 03 14:44:26.183779 2020] [core:notice] [pid 1:tid 140004244472960] AH00094: Command line: &#39;httpd -D FOREGROUND&#39;
10.0.2.2 - - [03/Mar/2020:14:44:46 +0000] &amp;quot;GET / HTTP/1.1&amp;quot; 200 45
10.0.2.2 - - [03/Mar/2020:14:44:55 +0000] &amp;quot;GET /yolo HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:02 +0000] &amp;quot;GET /yolo/test HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:07 +0000] &amp;quot;GET /yolo/cloud-people HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:12 +0000] &amp;quot;GET /yolo/cloud/ HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:16 +0000] &amp;quot;GET /static HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:20 +0000] &amp;quot;GET /lalalala HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:23 +0000] &amp;quot;GET /robots.txt HTTP/1.1&amp;quot; 404 196
10.0.2.2 - - [03/Mar/2020:14:45:27 +0000] &amp;quot;GET /?test=true HTTP/1.1&amp;quot; 200 45
10.0.2.2 - - [03/Mar/2020:14:45:30 +0000] &amp;quot;GET /?test=fals HTTP/1.1&amp;quot; 200 45
...
...
...
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;transform it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;docker logs d5b6b17ba444 | grep &#39;GET\|POST&#39; | awk &#39;{print &amp;quot;base.uri&amp;quot; $7}&#39;
base.uri/
base.uri/yolo
base.uri/yolo/test
base.uri/yolo/cloud-people
base.uri/yolo/cloud/
base.uri/static
base.uri/lalalala
base.uri/robots.txt
base.uri/?test=true
base.uri/?test=fals
base.uri/?test=false
base.uri/?search=a
base.uri/?search=ab
base.uri/?search=abc
base.uri/?search=abcd
base.uri/?search=abcde
base.uri/?search=abcdef
base.uri/result/76353220
base.uri/
base.uri/yolo
base.uri/yolo/test
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;aws-alb&#34;&gt;AWS Alb&lt;/h3&gt;
&lt;p&gt;Here is an example to pull in aws alb logs from yesterday from your s3 logs bucket.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ aws s3 --profile prd sync s3://your-logs-buckut/AWSLogs/youraccountnumber/elasticloadbalancing/yr-region-1/$(date --date yesterday &amp;quot;+%Y/%m/%d&amp;quot;) . ; gzip -d *
...
...
...
$ # export all GET requests to your logs file
$ cat * | grep &#39;GET&#39; |  awk &#39;{print $14}&#39; | sed &#39;s/.*:443/base.uri/g&#39; &amp;gt; alb-logs.txt
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Cross build your docker images</title>
      <link>https://aapjeisbaas.nl/post/cross-build-your-docker-images/</link>
      <pubDate>Wed, 05 Feb 2020 17:01:16 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/cross-build-your-docker-images/</guid>
      <description>&lt;p&gt;Now that ARM is getting bigger and AWS offering compute on ARM for bargain prices I was wondering if it would be feasible to prepare most of my projects for a platform shift to ARM.
The tool I like to use is 
&lt;a href=&#34;https://docs.docker.com/buildx/working-with-buildx/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;buildx&lt;/a&gt; at the moment it is still experimental but I&amp;rsquo;m having great success with it so far.
Docker Buildx is a CLI plugin that extends the docker command with the full support of the features provided by 
&lt;a href=&#34;https://github.com/moby/buildkit&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Moby BuildKit&lt;/a&gt; builder toolkit.
It provides the same user experience as docker build with many new features like creating scoped builder instances and building against multiple nodes concurrently.
And the most important thing to us it&amp;rsquo;s designed to work well with building for multiple platforms and not only for the architecture and operating system that the user invoking the build happens to run.&lt;/p&gt;
&lt;p&gt;You can build multi-platform images using three different strategies that are supported by Buildx and Dockerfiles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the QEMU emulation support in the kernel&lt;/li&gt;
&lt;li&gt;Building on multiple native nodes using the same builder instance&lt;/li&gt;
&lt;li&gt;Using a stage in Dockerfile to cross-compile to different architectures&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;ll focus on the first option, cross building with emulation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker buildx build --platform linux/amd64,linux/arm64 .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;This differs from platform to platform but one thing we all have in common is pipelines, so I&amp;rsquo;ve constructed a basic buildx setup for 
&lt;a href=&#34;https://gitlab.com/help/ci/yaml/README&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitLab CI/CD&lt;/a&gt;.
If you want to crossbuild from your local machine have a look at this 
&lt;a href=&#34;https://github.com/docker/buildx/#installing&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;install guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;pipeline&#34;&gt;Pipeline&lt;/h3&gt;
&lt;p&gt;This is a generic pipeline that takes in three variables that are pretty self explanatory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DOCKERHUB_USER&lt;/code&gt; : dockerhub username&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DOCKERHUB_PASS&lt;/code&gt; : dockerhub 
&lt;a href=&#34;https://docs.docker.com/docker-hub/access-tokens/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;access token&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DOCKERHUB_REPOSITORY&lt;/code&gt; full repository name like: 
&lt;a href=&#34;https://hub.docker.com/r/aapjeisbaas/builder&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;aapjeisbaas/builder&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;build:
  image: docker:latest
  services:
  - docker:dind
  stage: build
  script:
    # install depends
    - apk add curl jq

    # enable experimental buildx features
    - export DOCKER_BUILDKIT=1
    - export DOCKER_CLI_EXPERIMENTAL=enabled

    # Download latest buildx bin from github
    - mkdir -p ~/.docker/cli-plugins/
    - BUILDX_LATEST_BIN_URI=$(curl -s -L https://github.com/docker/buildx/releases/latest | grep &#39;linux-amd64&#39; | grep &#39;href&#39; | sed &#39;s/.*href=&amp;quot;/https:\/\/github.com/g; s/amd64&amp;quot;.*/amd64/g&#39;)
    - curl -s -L ${BUILDX_LATEST_BIN_URI} -o ~/.docker/cli-plugins/docker-buildx
    - chmod a+x ~/.docker/cli-plugins/docker-buildx

    # Get and run the latest docker/binfmt tag to use its qemu parts
    - BINFMT_IMAGE_TAG=$(curl -s https://registry.hub.docker.com/v2/repositories/docker/binfmt/tags | jq &#39;.results | sort_by(.last_updated)[-1].name&#39; -r)
    - docker run --rm --privileged docker/binfmt:${BINFMT_IMAGE_TAG}

    # create the multibuilder
    - docker buildx create --name multibuilder
    - docker buildx use multibuilder

    # login to a registry
    - docker login -u ${DOCKERHUB_USER} -p ${DOCKERHUB_PASS}

    # build the containers and push them to the registry then display the images
    - docker buildx build --platform linux/amd64,linux/arm64,linux/s390x,linux/386,linux/arm/v7,linux/arm/v6 -t ${DOCKERHUB_REPOSITORY}:latest . --push
    - docker buildx imagetools inspect ${DOCKERHUB_REPOSITORY}:latest

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here on out you can experiment with what platforms you can directly port to. Make sure you have a base image that supports multiarch like 
&lt;a href=&#34;https://hub.docker.com/_/alpine&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;alpine&lt;/a&gt; or one of these 
&lt;a href=&#34;https://hub.docker.com/search?category=base&amp;amp;source=verified&amp;amp;type=image&amp;amp;architecture=arm%2Carm64%2Camd64&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;base images&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;building-for-raspberry-pi&#34;&gt;Building for Raspberry pi&lt;/h2&gt;
&lt;p&gt;Make sure to include &lt;code&gt;linux/arm/v7&lt;/code&gt; for Raspberry Pi 2 and 3, older versions and pi zero use &lt;code&gt;linux/arm/v6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Raspberry pi 4 will be able to use &lt;code&gt;linux/arm/v7&lt;/code&gt; and in the future if 64 bit arm becomes available might support &lt;code&gt;linux/arm64&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;more-resources&#34;&gt;More resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/docker-library/official-images#architectures-other-than-amd64&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Docker official images - Architectures other than amd64? &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://www.docker.com/blog/docker-official-images-now-multi-platform/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Docker Blog - Docker Official Images are now Multi-platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://mirailabs.io/blog/multiarch-docker-with-buildx/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;mirailabs.io - Using multi-arch Docker images to support apps on any architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/docker/buildx/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Github buildx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://docs.docker.com/buildx/working-with-buildx/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Docker Docs - Buildx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/builder/-/jobs/427037991&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;aapjeisbaas builder - Multi arch pipeline in action&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Display AWS Cloudformation logs in pipelines</title>
      <link>https://aapjeisbaas.nl/post/display-aws-cloudformation-logs-in-pipelines/</link>
      <pubDate>Fri, 10 Jan 2020 00:16:46 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/display-aws-cloudformation-logs-in-pipelines/</guid>
      <description>&lt;p&gt;Using cloudformation is great, it gives you insane super powers but there are some small things that annoy me when using it in pipelines. My biggest issue is that it is not displaying the steps it takes in kind of real time&amp;rsquo;ish to stdout, so that&amp;rsquo;s where the following snippet will help.&lt;/p&gt;
&lt;h2 id=&#34;the-code&#34;&gt;The code&lt;/h2&gt;
&lt;script src=&#34;https://gitlab.com/snippets/1928583.js&#34;&gt;&lt;/script&gt;
&lt;h2 id=&#34;what-the-output-looks-like&#34;&gt;What the output looks like&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;[
    {
        &amp;quot;StackId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;EventId&amp;quot;: &amp;quot;5816a850-3333-11ea-83e6-02bbe9b74d60&amp;quot;,
        &amp;quot;StackName&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;LogicalResourceId&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;PhysicalResourceId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;ResourceType&amp;quot;: &amp;quot;AWS::CloudFormation::Stack&amp;quot;,
        &amp;quot;Timestamp&amp;quot;: &amp;quot;2020-01-09T22:57:00.232Z&amp;quot;,
        &amp;quot;ResourceStatus&amp;quot;: &amp;quot;UPDATE_IN_PROGRESS&amp;quot;,
        &amp;quot;ResourceStatusReason&amp;quot;: &amp;quot;User Initiated&amp;quot;
    }
]


[
    {
        &amp;quot;StackId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;EventId&amp;quot;: &amp;quot;5af0ca60-3333-11ea-94ed-0655dfaca74c&amp;quot;,
        &amp;quot;StackName&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;LogicalResourceId&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;PhysicalResourceId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;ResourceType&amp;quot;: &amp;quot;AWS::CloudFormation::Stack&amp;quot;,
        &amp;quot;Timestamp&amp;quot;: &amp;quot;2020-01-09T22:57:05.016Z&amp;quot;,
        &amp;quot;ResourceStatus&amp;quot;: &amp;quot;UPDATE_COMPLETE_CLEANUP_IN_PROGRESS&amp;quot;
    }
]


[
    {
        &amp;quot;StackId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;EventId&amp;quot;: &amp;quot;5b481220-3333-11ea-ac13-02d3b89dc706&amp;quot;,
        &amp;quot;StackName&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;LogicalResourceId&amp;quot;: &amp;quot;tech-blog&amp;quot;,
        &amp;quot;PhysicalResourceId&amp;quot;: &amp;quot;arn:aws:cloudformation:eu-west-1:368341477005:stack/tech-blog/2b819be0-28fe-11ea-8ab3-06e58f87e324&amp;quot;,
        &amp;quot;ResourceType&amp;quot;: &amp;quot;AWS::CloudFormation::Stack&amp;quot;,
        &amp;quot;Timestamp&amp;quot;: &amp;quot;2020-01-09T22:57:05.588Z&amp;quot;,
        &amp;quot;ResourceStatus&amp;quot;: &amp;quot;UPDATE_COMPLETE&amp;quot;
    }
]



Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - tech-blog
section_end:1578610649:build_script
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;if-you-want-it-all-and-you-want-it-now&#34;&gt;If you want it all and you want it now&lt;/h2&gt;
&lt;p&gt;This is the easiest way to use this snippet, I will try to keep functionality stable for this snippet but there are no guarantees that this wil work till the end of time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

# pull in cfn_wrapper
source &amp;lt;(curl -s https://gitlab.com/snippets/1928583/raw)

# deploy
cfn_wrapper aws cloudformation deploy --template-file &amp;quot;cloudformation.yml&amp;quot; \
  --stack-name $STACK --capabilities=&#39;CAPABILITY_IAM&#39; --no-fail-on-empty-changeset \
  --parameter-overrides \
  CfnParam=&amp;quot;$CFN_PARAM&amp;quot; \
  CfnVar=&amp;quot;$SOME_VAR&amp;quot;

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Create your own Wifi QR</title>
      <link>https://aapjeisbaas.nl/post/create-your-own-wifi-qr/</link>
      <pubDate>Sun, 22 Dec 2019 16:59:39 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/create-your-own-wifi-qr/</guid>
      <description>&lt;p&gt;If you&amp;rsquo;re tired of handing out wifi credentials on paper maybe this trick will help you out.&lt;/p&gt;
&lt;h3 id=&#34;example-with-qrencode&#34;&gt;Example with qrencode&lt;/h3&gt;
&lt;p&gt;This tool creates a qr in your terminal if that&amp;rsquo;s something you might need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   echo &#39;WIFI:S:The Promised Lan;T:WPA;P:Thisisthekey!;;&#39; | qrencode -t ansiutf8
█████████████████████████████████████
█████████████████████████████████████
████ ▄▄▄▄▄ █▄▀ ▀ ▄ ▀█ ▀███ ▄▄▄▄▄ ████
████ █   █ █   █▀▀▄▀▀  █ █ █   █ ████
████ █▄▄▄█ █▄█▀ ▄▄█ █▀█▄▄█ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄█ █ █ █▄▀▄█ █▄▄▄▄▄▄▄████
████▄   ▀▀▄█ █ ▄▀█▀▄█▀█ ▄▄▀█  ▄█ ████
████ ▀█ ▄▄▄▀   ▄█▀█▄█▄ █  ██▀▀█ ▄████
████▄▀██ ▀▄▀ ▄▄▀▄▄█ ▄▄▄▀▀   █ ▄▄ ████
█████▀ ▄ █▄▄▄ ▀█▄▀█▀  █▄█  ██  ▄▄████
████▄ ▄▀▄ ▄▀ ▄ ▄█  ▀ ▀█▀█▀▄▄▄▀ ▄█████
████▄█▀▀▀▀▄▀▀█  ▀▀▄ █▀▄▀▀██▀▄█  ▀████
█████▄█▄▄█▄▄ ▀█▄ █▀▄▄▄▀  ▄▄▄ ▀███████
████ ▄▄▄▄▄ ██▄▄█ ▄▄█▄ █  █▄█ ▀▄▄▀████
████ █   █ █▀▀▀ █ ██▄█ ▄▄▄ ▄▄  ██████
████ █▄▄▄█ █  █▄▀ ▄▀▄▀▄ ▀ ▀█▄▄█▀ ████
████▄▄▄▄▄▄▄█▄█▄▄▄█▄▄█████▄█▄█▄███████
█████████████████████████████████████
█████████████████████████████████████
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;a href=&#34;https://github.com/fukuchi/libqrencode&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;qrencode&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;make-it-good-looking-with-a-python-tool&#34;&gt;Make it good looking with a python tool&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://github.com/sylnsfar/qrcode&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;MyQR Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://pypi.org/project/MyQR/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;MyQR PyPi&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use this wifi logo for the best results:
&lt;img src=&#34;https://aapjeisbaas.nl/static/images/wifi-logo.png&#34; alt=&#34;wifi-logo&#34; title=&#34;Base WiFi logo&#34;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;myqr &#39;WIFI:S:The Promised Lan;T:WPA;P:Thisisthekey!;;&#39; -p wifi-logo.png -n wifi-qr.jpg 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;myqr-result&#34;&gt;MyQR result&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://aapjeisbaas.nl/static/images/wifi-qr.jpg&#34; alt=&#34;QR WiFi credentials&#34; title=&#34;MyQR example&#34;&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Update all docker images</title>
      <link>https://aapjeisbaas.nl/post/update-all-docker-images/</link>
      <pubDate>Sun, 22 Dec 2019 16:52:28 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/update-all-docker-images/</guid>
      <description>&lt;p&gt;When you&amp;rsquo;re running images with the tag latest on you local machine they might need some updates from time to time.
With this one liner you pull the newest version in of all the images you already have locally.&lt;/p&gt;
&lt;h2 id=&#34;update-all-images&#34;&gt;Update all images&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;for image in $(sudo docker image ls | awk &#39;{print $1}&#39; | grep -ve &amp;quot;none\|REPOSITORY&amp;quot; | sort | uniq); do sudo docker pull $image ;done
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;example&#34;&gt;example&lt;/h2&gt;
&lt;p&gt;This was on my media management machine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[svanbroekhoven@ibm729 ~]$ sudo docker image ls | awk &#39;{print $1}&#39; | grep -ve &amp;quot;none\|REPOSITORY&amp;quot; | sort | uniq
linuxserver/nzbget
linuxserver/plex
linuxserver/radarr
linuxserver/sonarr
linuxserver/tautulli
nvidia/cuda
ortho4xp
swarm
ubuntu
[svanbroekhoven@ibm729 ~]$ for image in $(sudo docker image ls | awk &#39;{print $1}&#39; | grep -ve &amp;quot;none\|REPOSITORY&amp;quot; | sort | uniq); do sudo docker pull $image ;done
Using default tag: latest
latest: Pulling from linuxserver/nzbget
Digest: sha256:4c433da392324f885dcb63b7a2aecaa72125a172ffb81761cf34dd3462dd7c65
Status: Image is up to date for linuxserver/nzbget:latest
docker.io/linuxserver/nzbget:latest
Using default tag: latest
latest: Pulling from linuxserver/plex
Digest: sha256:158865b029f109dc9f9460de792d78f1df056188ebf6a720a0bdc1ccde768f2a
Status: Image is up to date for linuxserver/plex:latest
docker.io/linuxserver/plex:latest
Using default tag: latest
latest: Pulling from linuxserver/radarr
Digest: sha256:8dbe3385f3bcd8d16558f6f9e31224f1d3ee994debb90454a7b827c745ff3c6d
Status: Image is up to date for linuxserver/radarr:latest
docker.io/linuxserver/radarr:latest
Using default tag: latest
latest: Pulling from linuxserver/sonarr
Digest: sha256:3c57fca1f14943219b2a4573464ab462ac60b8d8fd0396097b61ef7fc6366970
Status: Image is up to date for linuxserver/sonarr:latest
docker.io/linuxserver/sonarr:latest
Using default tag: latest
latest: Pulling from linuxserver/tautulli
Digest: sha256:9e3201491b9e6dce3ab476145a24371ce420e44ad042614ba46a69ffe3168672
Status: Image is up to date for linuxserver/tautulli:latest
docker.io/linuxserver/tautulli:latest
Using default tag: latest
latest: Pulling from nvidia/cuda
Digest: sha256:31e2a1ca7b0e1f678fb1dd0c985b4223273f7c0f3dbde60053b371e2a1aee2cd
Status: Image is up to date for nvidia/cuda:latest
docker.io/nvidia/cuda:latest
Using default tag: latest
Error response from daemon: pull access denied for ortho4xp, repository does not exist or may require &#39;docker login&#39;: denied: requested access to the resource is denied
Using default tag: latest
latest: Pulling from library/swarm
Digest: sha256:b866583a3b8791bcd705b7bc0fd94c66b695a1a2dbaeb5f59ed29940e5015dc8
Status: Image is up to date for swarm:latest
docker.io/library/swarm:latest
Using default tag: latest
latest: Pulling from library/ubuntu
Digest: sha256:250cc6f3f3ffc5cdaa9d8f4946ac79821aafb4d3afc93928f0de9336eba21aa4
Status: Image is up to date for ubuntu:latest
docker.io/library/ubuntu:latest
[svanbroekhoven@ibm729 ~]$
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Mass delete cloudformation stacks</title>
      <link>https://aapjeisbaas.nl/post/mass-delete-cloudformation-stacks/</link>
      <pubDate>Wed, 13 Nov 2019 11:32:37 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/mass-delete-cloudformation-stacks/</guid>
      <description>&lt;h3 id=&#34;the-oneliner&#34;&gt;The oneliner:&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# This will delete all stacks in the account connected to yourprofile with the string &amp;quot;delete-me&amp;quot; in the stackname
for StackName in $(aws --profile yourprofile cloudformation list-stacks --query &#39;StackSummaries[?contains(StackName, `delete-me`) == `true`] .StackName&#39; --output text); do echo $StackName ; aws --profile yourprofile cloudformation delete-stack --stack-name $StackName ; done
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Cloudformation &amp; Autoscaling</title>
      <link>https://aapjeisbaas.nl/talk/aws-cloudformation-en-autoscaling/</link>
      <pubDate>Tue, 01 Oct 2019 17:30:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/talk/aws-cloudformation-en-autoscaling/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Cloudformation and auto scaling, from the concepts to the details</title>
      <link>https://aapjeisbaas.nl/post/cloudformation-and-auto-scaling-from-the-concepts-to-the-details/</link>
      <pubDate>Wed, 11 Sep 2019 18:46:23 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/cloudformation-and-auto-scaling-from-the-concepts-to-the-details/</guid>
      <description>&lt;h2 id=&#34;before-you-start&#34;&gt;Before you start&lt;/h2&gt;
&lt;p&gt;Not every environment is ready for auto scaling, to be able to fully utilize these techniques your infra must comply with the following.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No local state on the disks of application servers.&lt;/li&gt;
&lt;li&gt;Redeployable images: ec2 asg/spot fleet (ami) or in the case of ecs you need docker images.
In case you want something to get started with, I would recommend using the linked below &amp;ldquo;hello world&amp;rdquo; docker image to get familiar with the way of working.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;
&lt;a href=&#34;https://hub.docker.com/r/aapjeisbaas/hello-container&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hello container&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;horizontal-vs-vertical-scaling&#34;&gt;Horizontal vs Vertical scaling&lt;/h3&gt;
&lt;p&gt;The basic definition which can be applied in almost all cases:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vertical scaling:&lt;/strong&gt; You increase the compute power of the node your code runs on.
This type of scaling is pretty limited and should be avoided is possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Horizontal scaling:&lt;/strong&gt; You increase the amount of nodes handling your traffic while keeping the per node size small.
This increases your resilience as your traffic increases and if a node fails impact is lower, this is the type of scaling we will take a look at.&lt;/p&gt;
&lt;h3 id=&#34;why-would-you-add-automated-scaling&#34;&gt;Why would you add automated scaling&lt;/h3&gt;
&lt;p&gt;The answer most people will tell you is; &amp;ldquo;To handle peak workloads.&amp;rdquo; but this is not the most important part of it.
After thinking it over I came to these 2 key reasons why auto scaling is the way to go.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Predictability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Predictability example:&lt;/strong&gt; When your application in it&amp;rsquo;s most minimal, predictable and stable form can handle a maximum of &lt;code&gt;X req/s&lt;/code&gt; adding more
nodes will  theoretically give you &lt;code&gt;NX req/s&lt;/code&gt; maximum throughput. Of course other bottlenecks will appear like database throughput, hold that thought
I will go into detail about automatically scaling your database later in this article.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Predictability example:&lt;/strong&gt; As your traffic increases you automatically add more vault tolerance, scaling out with small instances will decrease
the impact of a node failure. Node failure is a given, it will happen you just don&amp;rsquo;t know when, if you don&amp;rsquo;t have automated scaling you might turn
off the node because it is not functioning and put that load permanently on the rest of the cluster until the node is fixed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flexibility example:&lt;/strong&gt; If you optimize your application you can easily change the machine size as you are already adding and subtracting machines
regularly this is nothing out of the ordinary and could change many times a day. This makes size selecting based on trial and measure iterations
shorter and leads to faster results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flexibility example:&lt;/strong&gt; As your application and requirements changes the need may arise to deploy to a different part of the world to have lower
latency for your end users or migrate to a new location for whatever reason, it is pretty easy to add a new auto scaling resource in a different
location and reroute your traffic to the new location.&lt;/p&gt;
&lt;h3 id=&#34;what-to-scale&#34;&gt;What to scale&lt;/h3&gt;
&lt;p&gt;You can scale many parts of your infrastructure, in this writeup I focus on the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ECS&lt;/li&gt;
&lt;li&gt;EC2&lt;/li&gt;
&lt;li&gt;RDS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;types-of-scaling-triggers&#34;&gt;Types of scaling triggers&lt;/h3&gt;
&lt;h4 id=&#34;step-based-scaling&#34;&gt;&lt;strong&gt;Step based scaling&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;You define a set threshold if it is crossed scaling takes an action.&lt;/p&gt;
&lt;h4 id=&#34;target-tracking&#34;&gt;&lt;strong&gt;Target tracking&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;You define a number and a metric that should lower or raise by increasing or decreasing the amount of application servers and let
auto scale follow your &lt;strong&gt;desired number&lt;/strong&gt; This in the background sets 2 step based scaling rules 1 to scale out and another to scale in.&lt;/p&gt;
&lt;h3 id=&#34;what-to-base-the-scaling-on&#34;&gt;What to base the scaling on&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CPU Usage&lt;/li&gt;
&lt;li&gt;ALB traffic&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Cloudwatch metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-does-scaling-look-in-a-graph&#34;&gt;What does scaling look in a graph&lt;/h3&gt;
&lt;h4 id=&#34;no-scaling&#34;&gt;No scaling&lt;/h4&gt;
&lt;code&gt;
&lt;iframe width=&#34;1007&#34; height=&#34;541&#34; seamless frameborder=&#34;0&#34; scrolling=&#34;no&#34; src=&#34;https://docs.google.com/spreadsheets/d/e/2PACX-1vT0-gxv45boWWUfS5W0P4jAxIv9Tsp_OciH4nWUCuc9DO7_ZGsjUs6ZHNf2ShP-aROBxBDqCXeXHiHl/pubchart?oid=1606079267&amp;amp;format=interactive&#34;&gt;&lt;/iframe&gt;
&lt;/code&gt;
&lt;h4 id=&#34;perfect-scaling&#34;&gt;Perfect scaling&lt;/h4&gt;
&lt;code&gt;
&lt;iframe width=&#34;1009&#34; height=&#34;542&#34; seamless frameborder=&#34;0&#34; scrolling=&#34;no&#34; src=&#34;https://docs.google.com/spreadsheets/d/e/2PACX-1vT0-gxv45boWWUfS5W0P4jAxIv9Tsp_OciH4nWUCuc9DO7_ZGsjUs6ZHNf2ShP-aROBxBDqCXeXHiHl/pubchart?oid=1407214146&amp;amp;format=interactive&#34;&gt;&lt;/iframe&gt;
&lt;/code&gt;
&lt;h4 id=&#34;scaling-parameters&#34;&gt;Scaling parameters&lt;/h4&gt;
&lt;p&gt;This scaling examples was based on the following calculation: &lt;code&gt;B3=IF(A2 &amp;lt; 2*D2, 3, ROUNDUP(A2/D2)&lt;/code&gt; this roughly translates to the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Track 40 req/node with a minimum of 3 nodes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As auto scaling is reactive not predictive you first see the the traffic increase and in the next increment you see the instances respond.
The example just calculates the desired amount based on the previous data point, in AWS you have quite a bit more control on the triggers. (if you want)
Scaling steps are based on cloudwatch alarms, so if you can create a cloudwatch alarm for something you can use it to base your scaling on.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Period&lt;/strong&gt; is the length of time to evaluate the metric or expression to create each individual data point for an alarm. It is expressed in seconds.
If you choose one minute as the period, there is one data point every minute.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluation Period&lt;/strong&gt; is the number of the most recent periods, or data points, to evaluate when determining alarm state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Datapoints to Alarm&lt;/strong&gt; is the number of data points within the evaluation period that must be breaching to cause the alarm to go to the ALARM state.
The breaching data points don&amp;rsquo;t have to be consecutive, they just must all be within the last number of data points equal to Evaluation Period.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;how-to-write-the-scaling-rules-in-cloudformation&#34;&gt;How to write the scaling rules in Cloudformation&lt;/h3&gt;
&lt;p&gt;The most straight forward autoscaling can be found in

&lt;a href=&#34;https://docs.aws.amazon.com/en_pv/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;EC2 Auto Scaling groups&lt;/a&gt;,
lets first take a look at that and later on dive into more complex setups.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;AWSTemplateFormatVersion: 2010-09-09
Parameters:
  AMI:
    Type: String
  Subnets:
    Type: CommaDelimitedList
  AZs:
    Type: CommaDelimitedList
  PolicyTargetValue:
    Type: String
Resources:
  # what the instances should look like
  myLaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !Ref AMI
      InstanceType: t3.micro

  # group the instances
  myASGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      MaxSize: &#39;2&#39;
      AvailabilityZones: !Ref AZs
      VPCZoneIdentifier: !Ref Subnets
      MinSize: &#39;1&#39;
      LaunchConfigurationName: !Ref myLaunchConfig

  # Scale the group by this rule
  myCPUPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AutoScalingGroupName: !Ref myASGroup
      PolicyType: TargetTrackingScaling
      TargetTrackingConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ASGAverageCPUUtilization
        TargetValue: !Ref PolicyTargetValue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is probably the most compact way to write an auto scaling application stack based on machine CPU load as a variable, it uses 3 parts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resource to scale&lt;/li&gt;
&lt;li&gt;Grouping those resources&lt;/li&gt;
&lt;li&gt;Scaling rules for the group&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, this is not the case for all things we want to scale, the other type of scaling is based on 
&lt;a href=&#34;https://docs.aws.amazon.com/en_pv/autoscaling/application/userguide/what-is-application-auto-scaling.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;strong&gt;Application Auto Scaling&lt;/strong&gt;&lt;/a&gt; which is based on the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Resource to scale&lt;/li&gt;
&lt;li&gt;Grouping those resources&lt;/li&gt;
&lt;li&gt;Scaling rules for the group&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lets go trough one of my most elaborate self scaling templates, piece by piece.&lt;/p&gt;
&lt;p&gt;We start of with a ecs service, I left out the boilerplate resources like task definition and iam roles.
For the full templates check out the links at the bottom of this article.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  Service:
    Type: AWS::ECS::Service
    Properties:
      TaskDefinition: !Ref TaskDefinition
      DesiredCount: !Ref DesiredCount
      HealthCheckGracePeriodSeconds: 600
      LoadBalancers:
      - TargetGroupArn: !Ref TargetGroup
        ContainerPort: !Ref ContainerPort
        ContainerName: !Ref ContainerName
      Cluster: !Ref ContainerCluster
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
          - !Ref ContainerSG
          Subnets: !Ref Subnets
    DependsOn: ListenerRule
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the service that we want to scale, it is located in fargate so aside from soft limits of containers per account not much scaling restrictions.
Sorry I&amp;rsquo;ll not go deeper into building ECS services as it is a bit outside of the scope of this article.&lt;/p&gt;
&lt;p&gt;By default the ecs service resource has no scaling mechanism, so we create one with the 
&lt;a href=&#34;https://docs.aws.amazon.com/en_pv/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalabletarget.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;strong&gt;AWS::ApplicationAutoScaling::ScalableTarget&lt;/strong&gt;&lt;/a&gt; resource. This puts a virtual handle on a 
&lt;a href=&#34;https://docs.aws.amazon.com/en_pv/autoscaling/application/APIReference/API_RegisterScalableTarget.html#autoscaling-RegisterScalableTarget-request-ScalableDimension&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;dimension&lt;/a&gt; of your resource which will be controllable by auto scaling.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  ServiceScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MinCapacity: !Ref MinCount
      MaxCapacity: !Ref MaxCount
      ResourceId:
        # Example: service/MyECSCluster-AB12CDE3F4GH/MyECSService-AB12CDE3F4GH
        Fn::Join:
        - &#39;&#39;
        - - service/
          - Ref: ContainerCluster
          - &amp;quot;/&amp;quot;
          - Fn::GetAtt:
            - Service
            - Name
      RoleARN:
        Fn::GetAtt:
        - ApplicationAutoScalingRole
        - Arn
      ScalableDimension: ecs:service:DesiredCount
      # The all important scaling Dimension, in this case the amount of containers running in an ECS service.
      ServiceNamespace: ecs
      ScheduledActions:
        Fn::If:
        - NonProdCondition
        -
          - ScalableTargetAction:
              MinCapacity: 0
              MaxCapacity: 0
            Schedule: &amp;quot;cron(0 20 * * ? *)&amp;quot;
            ScheduledActionName: NightlyDown
          - ScalableTargetAction:
              MinCapacity: !Ref MinCount
              MaxCapacity: !Ref MaxCount
            Schedule: &amp;quot;cron(30 4 * * ? *)&amp;quot;
            ScheduledActionName: MorningUp
        - Ref: AWS::NoValue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;strong&gt;ScalableTarget&lt;/strong&gt; gives us a few more options to scale our service, as you can see in the example above I use a cron like scaling
action to turn off the service outside working hours if it isn&amp;rsquo;t a production deployment. Of course you can go all out with the cron
function and scale your application that way, but that&amp;rsquo;s not really automatic but can help if you have no trust in your auto scaling or
to give your auto scaling reasonable boundaries to operate in.&lt;/p&gt;
&lt;p&gt;So Let&amp;rsquo;s break it down into it&amp;rsquo;s components and where they get there data from&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  ServiceScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MinCapacity: !Ref MinCount
      MaxCapacity: !Ref MaxCount
      ResourceId:
        Fn::Join:
        - &#39;&#39;
        - - service/
          - Ref: ContainerCluster
          - &amp;quot;/&amp;quot;
          - Fn::GetAtt:
            - Service
            - Name
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Capacity:&lt;/strong&gt; Pretty straight forward, these are the upper and lower boundaries I import them from a variable with &lt;code&gt;!Ref&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ResourceId&lt;/strong&gt; This is in our case a &amp;ldquo;pointer&amp;rdquo; to the ecs service, but instead of a simple &lt;code&gt;!Ref&lt;/code&gt; we need a 
&lt;a href=&#34;https://docs.aws.amazon.com/en_pv/autoscaling/application/APIReference/API_RegisterScalableTarget.html#autoscaling-RegisterScalableTarget-request-ResourceId&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;RecourceId&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately we can&amp;rsquo;t just ask for this string, we need to construct it ourself so lets start with the end result and work our way
backwards on how to construct that string.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;service/container-cluster/container-service
  |     \                                /
  |      +--- Unique Identifier --------+
  |
  +-- resource type
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;      RoleARN:
        Fn::GetAtt:
        - ApplicationAutoScalingRole
        - Arn
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs
      ScheduledActions:
        Fn::If:
        - NonProdCondition
        -
          - ScalableTargetAction:
              MinCapacity: 0
              MaxCapacity: 0
            Schedule: &amp;quot;cron(0 20 * * ? *)&amp;quot;
            ScheduledActionName: NightlyDown
          - ScalableTargetAction:
              MinCapacity: !Ref MinCount
              MaxCapacity: !Ref MaxCount
            Schedule: &amp;quot;cron(30 4 * * ? *)&amp;quot;
            ScheduledActionName: MorningUp
        - Ref: AWS::NoValue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have the groundwork in place lets add some automation to scale based on &lt;strong&gt;target tracking&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  ServiceScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: !Sub ${AWS::StackName}
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: !Ref AutoscalingCpuTarget
        ScaleInCooldown: 180
        ScaleOutCooldown: 30
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the most basic auto scaling for ecs services and is pretty effective, especially if you consider how easy it is to implement.
Let&amp;rsquo;s deconstruct it&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  ServiceScalingPolicyReqPerM:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: &#39;req_per_minute&#39;
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: !Ref AutoscalingReqTarget
        ScaleInCooldown: 180
        ScaleOutCooldown: 30
        PredefinedMetricSpecification:
          PredefinedMetricType: ALBRequestCountPerTarget
          ResourceLabel: !Join [ &#39;/&#39;, [!ImportValue GulbFullName, !GetAtt TargetGroup.TargetGroupFullName] ]

          # https://docs.aws.amazon.com/en_pv/AWSCloudFormation/latest/UserGuide/aws-properties-applicationautoscaling-scalingpolicy-predefinedmetricspecification.html
          # RecourceLabel:
          # app/&amp;lt;load-balancer-name&amp;gt;/&amp;lt;load-balancer-id&amp;gt;/targetgroup/&amp;lt;target-group-name&amp;gt;/&amp;lt;target-group-id&amp;gt;

          # arn:aws:elasticloadbalancing:eu-west-1:716268079250:loadbalancer/app/gulb/8a5d787993154763
          # app/gulb/8a5d787993154763
          # !ImportValue GulbFullName

          # arn:aws:elasticloadbalancing:eu-west-1:716268079250:targetgroup/iris-Targe-2ITUDAYHYT85/17d40dfaff1a60e6
          # targetgroup/iris-Targe-2ITUDAYHYT85/17d40dfaff1a60e6
          # !GetAtt TargetGroup.TargetGroupFullName

          # RecourceLabel:
          # app/gulb/8a5d787993154763/targetgroup/iris-Targe-2ITUDAYHYT85/17d40dfaff1a60e6
          # !Join [ &#39;&#39;, [!ImportValue GulbFullName, !GetAtt TargetGroup.TargetGroupFullName] ]
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Add target tracking auto scale to your aws ecs service</title>
      <link>https://aapjeisbaas.nl/post/add-target-tracking-auto-scale-to-your-aws-ecs-service/</link>
      <pubDate>Mon, 04 Feb 2019 11:59:01 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/add-target-tracking-auto-scale-to-your-aws-ecs-service/</guid>
      <description>&lt;p&gt;This cloudformation snippet will add auto scale to your existing cloudformation based ecs service.&lt;/p&gt;
&lt;p&gt;To connect it to your service you may need to edit the folowing vars &lt;strong&gt;ServiceScalingTarget:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Service&lt;/strong&gt; This is the name of the cloudformation block that defines your ecs service&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ref: ContainerCluster&lt;/strong&gt; This should pull in the ecs cluster name I define this in a param: ContainerCluster&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  ApplicationAutoScalingRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: [application-autoscaling.amazonaws.com]
          Action: [&#39;sts:AssumeRole&#39;]
      Path: /
      Policies:
      -
        PolicyName: ECSBlogScalingRole
        PolicyDocument:
          Statement:
          - Effect: Allow
            Action:
            - ecs:UpdateService
            - ecs:DescribeServices
            - application-autoscaling:*
            - cloudwatch:DescribeAlarms
            - cloudwatch:GetMetricStatistics
            Resource: &amp;quot;*&amp;quot;

  ServiceScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: 20
      MinCapacity: 1
      ResourceId:
        Fn::Join:
        - &#39;&#39;
        - - service/
          - Ref: ContainerCluster
          - &amp;quot;/&amp;quot;
          - Fn::GetAtt:
            - Service
            - Name
      RoleARN:
        Fn::GetAtt:
        - ApplicationAutoScalingRole
        - Arn
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs

  ServiceScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: !Sub ${AWS::StackName}
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref ServiceScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 50.0
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Hyper fast wordpress</title>
      <link>https://aapjeisbaas.nl/post/hyper-fast-wordpress/</link>
      <pubDate>Mon, 15 Oct 2018 22:56:54 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/hyper-fast-wordpress/</guid>
      <description>&lt;h4 id=&#34;how-to-speedup-wordpress-without-without-changing-your-website&#34;&gt;How to speedup wordpress without without changing your website.&lt;/h4&gt;
&lt;p&gt;Not one of my regular deeply technical posts but not any less valueable.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;re probably here because you have a Wordpress based website and are not too impressed with the performance.
There are a few approaches to make your content Hyper Fast ;-)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;use a cache plugin with minimal changes to the site&lt;/li&gt;
&lt;li&gt;use a cache plugin to build a static version of the site&lt;/li&gt;
&lt;li&gt;migrate content to a static site generator (
&lt;a href=&#34;https://gohugo.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;hugo&lt;/a&gt; / 
&lt;a href=&#34;https://blog.getpelican.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pelican&lt;/a&gt; / 
&lt;a href=&#34;https://jekyllrb.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;jekyll&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;cache your site in a cdn and purge when you update content&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Today we are looking into the last one, cache everything at a cdn and purge on updates.
Let&amp;rsquo;s go trough the steps:&lt;/p&gt;
&lt;h2 id=&#34;register-at-cloudflarecomhttpsdashcloudflarecomsign-up&#34;&gt;
&lt;a href=&#34;https://dash.cloudflare.com/sign-up&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Register at cloudflare.com&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where your content will be cached.&lt;/p&gt;
&lt;h2 id=&#34;setup-dns-and-cdn&#34;&gt;Setup DNS and CDN&lt;/h2&gt;
&lt;p&gt;After you&amp;rsquo;ve created your account you need to start using cloudflare&amp;rsquo;s dns servers.
This if done carefully, can be done without any downtime or hickups.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the domain in your cloudflare account (this is in the sign up procedure if I recall correctly)&lt;/li&gt;
&lt;li&gt;Copy all the DNS records from your existing DNS provider to cloudflare trough their excelent web interface. Except the NS records, we&amp;rsquo;ll do these last.&lt;/li&gt;
&lt;li&gt;Make sure you have no orange clouds behind your records, we will enable their cdn later.&lt;/li&gt;
&lt;li&gt;You can make some dns requests with &lt;code&gt;dig&lt;/code&gt; to compare old and new dns servers: (you can find your cloudflare dns servers in the web pannel)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;domain=&amp;quot;aapjeisbaas.nl&amp;quot;
# find your current nameservers:
dig +short ns $domain

# find your cloudflare nameservers:
dig +short ns $domain @ns.cloudflare.com.

# compare some records
dig +short a $domain 
dig +short a $domain @ns.cloudflare.com.

dig +short a www.$domain 
dig +short a www.$domain @ns.cloudflare.com.

dig +short mx $domain 
dig +short mx $domain @ns.cloudflare.com.

dig +short txt $domain 
dig +short txt $domain @ns.cloudflare.com.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the results from your current and cloudflare&amp;rsquo;s dns servers match you can carry on to the next step.&lt;/p&gt;
&lt;h2 id=&#34;start-using-cloudflares-nameservers&#34;&gt;Start using cloudflare&amp;rsquo;s nameservers&lt;/h2&gt;
&lt;p&gt;So your old and new DNS setup match, now we need to replace the NS records at your registrar.
Most of the time the place where you bought your domain has a way to change your nameservers.
Change them to the records provided by cloudflare, you can double check with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# find your cloudflare nameservers:
dig +short ns $domain @ns.cloudflare.com.

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check if you changed your Nameservers correctly at your registrar use the following commands, keep in mind that this may take a few hours to be reflected in the results:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# output should contain cloudflare nameservers
whois $domain

# output should look like:
# aida.ns.cloudflare.com. dns.cloudflare.com. 2029044598 10000 2400 604800 3600
# \_ Your 1st nameserver   \_ contact          \_ serial  \     \    \      \_ ttl
#                                                          \     \    \_ expire
#  Changes in your dns wil increase the serial              \     \_ retry
#                                                            \_ refresh
dig +short soa $domain
dig +short soa $domain @1.1.1.1
dig +short soa $domain @8.8.8.8
dig +short soa $domain @4.2.2.2


# output should only contain your cloudflare nameservers
dig +short ns $domain
dig +short ns $domain @1.1.1.1
dig +short ns $domain @8.8.8.8
dig +short ns $domain @4.2.2.2

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;test-it&#34;&gt;Test it&lt;/h2&gt;
&lt;p&gt;Try to send some mail cross domains, recieve some mail cross domains. visit your website from multiple machines and devices.&lt;/p&gt;
&lt;h2 id=&#34;enable-cloudflare&#34;&gt;Enable cloudflare&lt;/h2&gt;
&lt;p&gt;To use cloudflare&amp;rsquo;s CDN goodness simply click on the cloud behind a DNS record in the dns admin interface.
When it is orange with the arrow trough it, it is active.&lt;/p&gt;
&lt;h2 id=&#34;install-2-plugins-to-get-the-most-out-of-this-setup&#34;&gt;Install 2 plugins to get the most out of this setup&lt;/h2&gt;
&lt;p&gt;To cache your pages at cloudflare for as long as you want:
&lt;a href=&#34;https://wordpress.org/plugins/cache-control/&#34;&gt;https://wordpress.org/plugins/cache-control/&lt;/a&gt;
Increase s-maxage to 604800 to cache for 1 week, you can leave the other options default.&lt;/p&gt;
&lt;p&gt;To clear the cache when you update your site:
&lt;a href=&#34;https://wordpress.org/plugins/cloudflare/&#34;&gt;https://wordpress.org/plugins/cloudflare/&lt;/a&gt;
After install connect it with your cloudflare account to allow your site to clear the cache.
When your api connection is set up click the &lt;code&gt;optimize for wordpress&lt;/code&gt; button to tweak some settings.&lt;/p&gt;
&lt;h2 id=&#34;take-it-up-a-notch&#34;&gt;Take it up a notch&lt;/h2&gt;
&lt;p&gt;To cache all the pages you serve create a page rule for &lt;code&gt;*testdomain.com/*&lt;/code&gt; with setting &lt;code&gt;Cache level: Cache Everything&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you have any questions feel free to leave them below, if you want personal advice on how to optimize your specific website 
&lt;a href=&#34;mailto:stein@aapjeisbaas.nl&#34;&gt;contact me&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Get ec2 arn for local ec2 machine with python</title>
      <link>https://aapjeisbaas.nl/post/get-ec2-arn-for-local-ec2-machine-with-python/</link>
      <pubDate>Sun, 16 Sep 2018 16:22:45 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/get-ec2-arn-for-local-ec2-machine-with-python/</guid>
      <description>&lt;p&gt;A super minimal urllib based arn constructor based on local metadata&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;import urllib.request

# local ec2 arn:
f = urllib.request.urlopen(&#39;http://169.254.169.254/latest/meta-data/placement/availability-zone&#39;)
az = f.read().decode(&#39;utf-8&#39;)
f = urllib.request.urlopen(&#39;http://169.254.169.254/latest/meta-data/instance-id&#39;)
instance = f.read().decode(&#39;utf-8&#39;)
arn = &#39;arn:aws:ec2:&#39; + az + &#39;:instance/&#39; + instance
print(arn)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returns arn as used by the &lt;code&gt;EC2 Spot Instance Interruption Warning&lt;/code&gt; event&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
  &amp;quot;version&amp;quot;: &amp;quot;0&amp;quot;,
  &amp;quot;id&amp;quot;: &amp;quot;03a51c99-95cb-c6e8-03a8-3ce3ac402d64&amp;quot;,
  &amp;quot;detail-type&amp;quot;: &amp;quot;EC2 Spot Instance Interruption Warning&amp;quot;,
  &amp;quot;source&amp;quot;: &amp;quot;aws.ec2&amp;quot;,
  &amp;quot;account&amp;quot;: &amp;quot;763603311319&amp;quot;,
  &amp;quot;time&amp;quot;: &amp;quot;2018-09-16T13:17:10Z&amp;quot;,
  &amp;quot;region&amp;quot;: &amp;quot;eu-west-1&amp;quot;,
  &amp;quot;resources&amp;quot;: [
    &amp;quot;arn:aws:ec2:eu-west-1b:instance/i-05493b58ebcba0c43&amp;quot;
  ],
  &amp;quot;detail&amp;quot;: {
    &amp;quot;instance-id&amp;quot;: &amp;quot;i-05493b58ebcba0c43&amp;quot;,
    &amp;quot;instance-action&amp;quot;: &amp;quot;terminate&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>aws cli search for latest ami</title>
      <link>https://aapjeisbaas.nl/post/aws-cli-search-for-latest-ami/</link>
      <pubDate>Fri, 24 Aug 2018 21:28:27 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/aws-cli-search-for-latest-ami/</guid>
      <description>&lt;p&gt;This simple oneliner gets you the latest ami in a given region for a specific search term.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# get the latest ecs-optimized amazon ami in eu-west-1
aws ec2 describe-images --region eu-west-1 --owners amazon --filters &amp;quot;Name=name,Values=*amazon-ecs-optimized&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot; --query &#39;sort_by(Images, &amp;amp;CreationDate)[-1].ImageId&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So lets break it down to give you what you need to get your favorite ami.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Get the full ami list for name search term &amp;quot;*amazon-ecs-optimized&amp;quot;
aws ec2 describe-images --region eu-west-1 --owners amazon --filters &amp;quot;Name=name,Values=*amazon-ecs-optimized&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot;

# this is searching for just the name, you could of course replace or expand this with other atributes like:
aws ec2 describe-images --region eu-west-1 --owners amazon --filters &amp;quot;Name=name,Values=*amazon-ecs-optimized&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot; &amp;quot;Name=block-device-mapping.volume-type,Values=gp2&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the full filter atributes list can be found in the 
&lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-images.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;aws docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now lets take a look at the structure of the returned json&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;aws ec2 describe-images --region eu-west-1 --owners amazon --filters &amp;quot;Name=name,Values=*amazon-ecs-optimized&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot; | head

# example output:
{
    &amp;quot;Images&amp;quot;: [
        {
            &amp;quot;Architecture&amp;quot;: &amp;quot;x86_64&amp;quot;,
            &amp;quot;CreationDate&amp;quot;: &amp;quot;2017-11-10T19:59:12.000Z&amp;quot;,
            &amp;quot;ImageId&amp;quot;: &amp;quot;ami-014ae578&amp;quot;,
            &amp;quot;ImageLocation&amp;quot;: &amp;quot;amazon/amzn-ami-2017.09.b-amazon-ecs-optimized&amp;quot;,
            &amp;quot;ImageType&amp;quot;: &amp;quot;machine&amp;quot;,
            &amp;quot;Public&amp;quot;: true,
            &amp;quot;OwnerId&amp;quot;: &amp;quot;591542846629&amp;quot;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returnes an Images array with your results (ami with all the juicy metadata)&lt;/p&gt;
&lt;p&gt;I use the built in query tool to grok this data (the query is written in JMESPath),
to get at the object that we want we take the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sort an array&lt;/li&gt;
&lt;li&gt;which one: the Images array&lt;/li&gt;
&lt;li&gt;what to sort that data by the value of CreationDate&lt;/li&gt;
&lt;li&gt;return the last object&lt;/li&gt;
&lt;li&gt;retrieve .ImageId from the returned object&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;return-objects-from-the-images-array-sorted-by-value-of-date&#34;&gt;return objects from the Images array sorted by value of date&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;--query &#39;sort_by(Images, &amp;amp;CreationDate)&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;just-get-the--1-object--1-last-0-first&#34;&gt;just get the -1 object (-1 last, 0 first)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;--query &#39;sort_by(Images, &amp;amp;CreationDate)[-1]&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;get-the-ami-id-from-the-returned-object&#34;&gt;get the ami id from the returned object&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;--query &#39;sort_by(Images, &amp;amp;CreationDate)[-1].ImageId&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Testing these query&amp;rsquo;s can be done over here: 
&lt;a href=&#34;http://jmespath.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;JMESPath&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now put all this together and you get:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;$ aws ec2 describe-images --owners amazon --filters &amp;quot;Name=name,Values=*amazon-ecs-optimized&amp;quot; &amp;quot;Name=architecture,Values=x86_64&amp;quot; --query &#39;sort_by(Images, &amp;amp;CreationDate)[-1].ImageId&#39;
&amp;quot;ami-0af844a965e5738db&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;elastic-beanstalk-latest-php-solutionstackname-finder&#34;&gt;Elastic beanstalk latest PHP SolutionStackName finder&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;aws --region us-east-2 elasticbeanstalk list-available-solution-stacks --query &#39;SolutionStackDetails[?contains(SolutionStackName, `PHP`) == `true`].[SolutionStackName][-1]&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Figuring this out took me way too long so thought I&amp;rsquo;d share it here&lt;/p&gt;
&lt;p&gt;More examples I found in a blogpost of 
&lt;a href=&#34;https://opensourceconnections.com/blog/2015/07/27/advanced-aws-cli-jmespath-query/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;OpenSource Connections&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Presentaties en trainingen</title>
      <link>https://aapjeisbaas.nl/aapjeisbaas/</link>
      <pubDate>Thu, 28 Jun 2018 00:00:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/aapjeisbaas/</guid>
      <description>&lt;p&gt;Allereerst wat of wie is 
&lt;a href=&#34;https://www.linkedin.com/company/aapje-is-baas/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Aapje is Baas&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;Aapje is Baas is een eenmanszaak van 
&lt;a href=&#34;https://aapjeisbaas.nl/authors/aapjeisbaas/&#34;&gt;Stein van Broekhoven&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;De bedrijfsnaam is ontstaan door een succesvolle test die toen dit domein registreerde en het is blijven hangen. 😉&lt;/p&gt;
&lt;p&gt;Heeft u een onderwerp waar uw organisatie in kan groeien en wilt u hier een presentatie over bij u op locatie?
Ik help u hier graag bij en heb een breed kennis gebied waarover ik graag vertel en uitleg.&lt;/p&gt;
&lt;h2 id=&#34;onderwerpen&#34;&gt;Onderwerpen&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href=&#34;https://aapjeisbaas.nl/tags/pipeline/&#34;&gt;CI/CD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://aapjeisbaas.nl/tags/aws/&#34;&gt;AWS Cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://aapjeisbaas.nl/tags/docker/&#34;&gt;Microservice architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://aapjeisbaas.nl/tags/tutorial/&#34;&gt;KISS IT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href=&#34;https://aapjeisbaas.nl/tags/cloudformation/&#34;&gt;Infra as Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Compliance in moderne infra&lt;/li&gt;
&lt;li&gt;Release cycle versnellen&lt;/li&gt;
&lt;li&gt;Site Reliability Engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wilt u een afspraak maken om te kijken wat ik voor u kan beteken, neem gerust 
&lt;a href=&#34;https://aapjeisbaas.nl/#contact&#34;&gt;contact&lt;/a&gt; op.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Install vim without X11 on FreeBSD</title>
      <link>https://aapjeisbaas.nl/post/install-vim-without-x11-on-freebsd/</link>
      <pubDate>Thu, 28 Sep 2017 15:09:32 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/install-vim-without-x11-on-freebsd/</guid>
      <description>&lt;p&gt;I can work with vi, but after using vim for years I just can&amp;rsquo;t work without it. The only problem is, if you install vim with &lt;code&gt;pkg install vim&lt;/code&gt; it installs X11 and related crap.&lt;/p&gt;
&lt;p&gt;This should be done as root.&lt;/p&gt;
&lt;p&gt;First update portsnap:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# get it
portsnap fetch update

# extract it
portsnap extract

root@bsd-test:~ # cd /usr/ports/editors/vim
root@bsd-test:/usr/ports/editors/vim # ll
total 27
-rw-r--r--  1 root  wheel  7022 Sep 23 23:06 Makefile
-rw-r--r--  1 root  wheel   175 Sep 23 23:06 distinfo
drwxr-xr-x  2 root  wheel     6 Sep 28 14:25 files/
-rw-r--r--  1 root  wheel   601 Jun 14  2015 pkg-descr
-rw-r--r--  1 root  wheel  8897 Jan  9  2017 pkg-plist
root@bsd-test:/usr/ports/editors/vim # make WITHOUT_X11=yes install clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the option questions I only changed the first one to console and not using any graphic stuff.&lt;/p&gt;
&lt;p&gt;It complained it couldn&amp;rsquo;t find perl5.24.3, so I installed it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;root@bsd-test:/usr/ports/editors/vim # cd /usr/ports/lang/perl5.24
root@bsd-test:/usr/ports/lang/perl5.24 # make install clean
...

# it crashed because ther was an old perl installed
root@bsd-test:/usr/ports/lang/perl5.24 # make reinstall
...

# when it was done I went back to the vim port and started the make again
root@bsd-test:/usr/ports/lang/perl5.24 # cd - 
root@bsd-test:/usr/ports/editors/vim # make WITHOUT_X11=yes install clean
...

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can use vim :-D&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Setup Oh My Zsh on FreeBSD</title>
      <link>https://aapjeisbaas.nl/post/setup-oh-my-zsh-on-freebsd/</link>
      <pubDate>Thu, 28 Sep 2017 13:33:49 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/setup-oh-my-zsh-on-freebsd/</guid>
      <description>&lt;p&gt;&amp;ldquo;Oh My Zsh is an open source, community-driven framework for managing your zsh configuration.&lt;/p&gt;
&lt;p&gt;Sounds boring. Let&amp;rsquo;s try again.&lt;/p&gt;
&lt;p&gt;Oh My Zsh will not make you a 10x developer&amp;hellip;but you might feel like one.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;First of we need some prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pkg&lt;/li&gt;
&lt;li&gt;zsh&lt;/li&gt;
&lt;li&gt;curl&lt;/li&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s get this install going.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# become root
su -

# check if pkg is installed if it is not it will promt you with an easy installer.
root@bsd-test:~ # pkg help
The package management tool is not yet installed on your system.
Do you want to fetch and install it now? [y/N]: y
Bootstrapping pkg from pkg+http://pkg.FreeBSD.org/FreeBSD:11:amd64/quarterly, please wait...
Verifying signature with trusted certificate pkg.freebsd.org.2013102301... done
Installing pkg-1.10.1...
Extracting pkg-1.10.1: 100%
Usage: pkg [-v] [-d] [-l] [-N] [-j &amp;lt;jail name or id&amp;gt;|-c &amp;lt;chroot path&amp;gt;|-r &amp;lt;rootdir&amp;gt;] [-C &amp;lt;configuration file&amp;gt;] [-R &amp;lt;repo config dir&amp;gt;] [-o var=value] [-4|-6] &amp;lt;command&amp;gt; [&amp;lt;args&amp;gt;]
Global options supported:
...
...
...

root@bsd-test:~ # pkg install zsh curl git
Updating FreeBSD repository catalogue...
pkg: Repository FreeBSD load error: access repo file(/var/db/pkg/repo-FreeBSD.sqlite) failed: No such file or directory
Fetching meta.txz: 100%    944 B   0.9kB/s    00:01
Fetching packagesite.txz: 100%    6 MiB   2.0MB/s    00:03
Processing entries: 100%
FreeBSD repository update completed. 26598 packages processed.
All repositories are up to date.
Updating database digests format: 100%
...
...
...
root@bsd-test:~ #

# now we can set the zsh as the default shell for our user

root@bsd-test:~ # chsh -s zsh svanbroekhoven
chsh: user information updated

# Close the ssh session and reopen it.
# You will be greated with a zsh welcome message, close this with &amp;quot;q&amp;quot;
# Lets pull in oh-my-zsh

bsd-test% sh -c &amp;quot;$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)&amp;quot;
Cloning Oh My Zsh...
Cloning into &#39;/home/svanbroekhoven/.oh-my-zsh&#39;...
remote: Counting objects: 831, done.
remote: Compressing objects: 100% (700/700), done.
remote: Total 831 (delta 14), reused 777 (delta 10), pack-reused 0
Receiving objects: 100% (831/831), 568.74 KiB | 988.00 KiB/s, done.
Resolving deltas: 100% (14/14), done.
Looking for an existing zsh config...
Using the Oh My Zsh template file and adding it to ~/.zshrc
         __                                     __
  ____  / /_     ____ ___  __  __   ____  _____/ /_
 / __ \/ __ \   / __ `__ \/ / / /  /_  / / ___/ __ \
/ /_/ / / / /  / / / / / / /_/ /    / /_(__  ) / / /
\____/_/ /_/  /_/ /_/ /_/\__, /    /___/____/_/ /_/
                        /____/                       ....is now installed!


Please look over the ~/.zshrc file to select plugins, themes, and options.

p.s. Follow us at https://twitter.com/ohmyzsh.

p.p.s. Get stickers and t-shirts at http://shop.planetargon.com.

➜  ~ 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s all, now find yourself a fancy 
&lt;a href=&#34;https://github.com/robbyrussell/oh-my-zsh/wiki/themes&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;theme&lt;/a&gt; and place it in: &lt;code&gt;~/.zshrc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The default is &lt;code&gt;ZSH_THEME=robbyrussell&lt;/code&gt;, I often use &lt;code&gt;ZSH_THEME=&amp;quot;aussiegeek&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;To view the results of your edit simply reopen your ssh session.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Reduce tty count on FreeBSD</title>
      <link>https://aapjeisbaas.nl/post/reduce-tty-count-on-freebsd/</link>
      <pubDate>Thu, 28 Sep 2017 12:55:27 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/reduce-tty-count-on-freebsd/</guid>
      <description>&lt;p&gt;After installing FreeBSD I noticed lots of tty&amp;rsquo;s open that I&amp;rsquo;ll probably never use so lets disable them with this small script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh
sed -i &#39;&#39; -e&#39;s|ttyv0.*|ttyv0	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	on  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv1.*|ttyv1	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	on  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv2.*|ttyv2	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv3.*|ttyv3	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv4.*|ttyv4	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv5.*|ttyv5	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv6.*|ttyv6	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
sed -i &#39;&#39; -e&#39;s|ttyv7.*|ttyv7	&amp;quot;/usr/libexec/getty Pc&amp;quot;		xterm	off  secure|&#39; /etc/ttys
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reboot your machine and voila, less ttys filling up your ps list. ;-)&lt;/p&gt;
&lt;p&gt;Of course you can edit the &lt;code&gt;/etc/ttys&lt;/code&gt; file by hand if you like.&lt;/p&gt;
&lt;p&gt;I originally found this script on the 
&lt;a href=&#34;https://forums.freebsd.org/threads/41154/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;FreeBSD Forum.&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Top like interface for lsof</title>
      <link>https://aapjeisbaas.nl/post/top-like-interface-for-lsof/</link>
      <pubDate>Tue, 26 Sep 2017 21:32:44 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/top-like-interface-for-lsof/</guid>
      <description>&lt;p&gt;If you&amp;rsquo;re curious which pids are using loads of open files this script displays just that.&lt;/p&gt;
&lt;h4 id=&#34;example&#34;&gt;Example:&lt;/h4&gt;
&lt;p&gt;The following overview will update +/- every second.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;      OPEN / LIMIT      NAME
     32568 / 4096       chrome          (PID  1365)
     14473 / 4096       spotify         (PID  1071)
     12530 / 4096       nylas           (PID  8430)
     10952 / 4096       skypeforlinux   (PID 26476)
      9471 / 4096       skypeforlinux   (PID 26513)
      5360 / 4096       Nylas_Mail_defa (PID  8914)
      4998 / 4096       gnome-shell     (PID   823)
      3726 / 4096       Nylas_Mail_work (PID  8473)
      3528 / 4096       spotify         (PID  1222)
      3105 / 4096       Enpass          (PID  1065)
      2898 / 4096       chrome          (PID  1450)
      2864 / 4096       Nylas_Mail_empt (PID  6152)
      2752 / 4096       tracker-extract (PID  1076)
      2528 / 4096       telegram-deskto (PID 29313)
      1908 / 4096       TeamViewer.exe  (PID 17517)
      1480 / 4096       nylas           (PID 27110)
      1480 / 4096       nylas           (PID 27099)
      1480 / 4096       nylas           (PID 27092)
      1132 / 4096       nylas           (PID  8458)
       890 / 4096       spotify         (PID  1207)
       870 / 4096       gsd-media-keys  (PID  1000)
       858 / 4096       TVGuiSlave.64   (PID 17658)
       845 / 4096       skypeforlinux   (PID 26501)
       810 / 4096       TVGuiDelegate   (PID 17659)
       810 / 4096       solaar          (PID  1070)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/sysop-tools/blob/master/lsof-top&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;lsof-top&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
# It is pretty clear this is a crude way to do it, and yes it started as a quick one liner.


while true; do
  RESULT=$(
    lsof -n 2&amp;gt;/dev/null | awk &#39;{print $2}&#39; | sort | uniq -c | sort -nr | head -25 | while read nr pid ; do
    printf &amp;quot;%10d / %-10d %-15s (PID %5s)\n&amp;quot; $nr $(cat /proc/$pid/limits | grep &#39;open files&#39; | awk &#39;{print $5}&#39;) $(ps -p $pid -o comm= | sed &#39;s/\ /_/g&#39; ) $pid
    done
  )
  clear
  echo &amp;quot;      OPEN / LIMIT      NAME&amp;quot;
  echo -n &amp;quot;$RESULT&amp;quot;
  sleep 1
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;list all open files&lt;/li&gt;
&lt;li&gt;clean the lines just keep the pid&lt;/li&gt;
&lt;li&gt;count the occurences of pid in the open file list&lt;/li&gt;
&lt;li&gt;create a line for the top 25 open file pids in a &lt;code&gt;RESULT&lt;/code&gt; var&lt;/li&gt;
&lt;li&gt;clear the screen&lt;/li&gt;
&lt;li&gt;echo the &lt;code&gt;RESULT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Quick and easy self signed ssl certificate</title>
      <link>https://aapjeisbaas.nl/post/quick-and-easy-self-signed-ssl-certificate/</link>
      <pubDate>Fri, 22 Sep 2017 22:46:01 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/quick-and-easy-self-signed-ssl-certificate/</guid>
      <description>&lt;p&gt;In many environments you can use ssl encryption, and you may skip the encryption because generating a cert or getting a lets encrypt cert would add more time to the thing your are working on.
To overcome this security issue I wrote 
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/sysop-tools/blob/master/selfsigned.sh&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;a small script&lt;/a&gt; to create a crt and key file.&lt;/p&gt;
&lt;h4 id=&#34;selfsignedshhttpsgitlabcomaapjeisbaassysop-toolsblobmasterselfsignedsh&#34;&gt;
&lt;a href=&#34;https://gitlab.com/aapjeisbaas/sysop-tools/blob/master/selfsigned.sh&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;selfsigned.sh&lt;/a&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

# fast ssl cert without ca

ENDPOINT=$1

openssl genrsa -out $ENDPOINT.key 2048
openssl req -new -x509 -key $ENDPOINT.key -out $ENDPOINT.crt -days 3650 -subj /CN=$ENDPOINT
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&#34;usage&#34;&gt;Usage:&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt; ./selfsigned.sh domain.tld
Generating RSA private key, 2048 bit long modulus
..............+++
....+++
e is 65537 (0x010001)

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This created 2 files &lt;code&gt;domain.tld.key&lt;/code&gt; and &lt;code&gt;domain.tld.crt&lt;/code&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Multiple flavours of nginx http to https redirects</title>
      <link>https://aapjeisbaas.nl/post/multiple-flavours-of-nginx-http-to-https-redirects/</link>
      <pubDate>Thu, 21 Sep 2017 00:36:25 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/multiple-flavours-of-nginx-http-to-https-redirects/</guid>
      <description>&lt;p&gt;Redirects in nginx are really powerful, here are some cool and common examples.&lt;/p&gt;
&lt;h3 id=&#34;all-domains-http--https-and-remove-www&#34;&gt;all domains http &amp;gt; https and remove www&lt;/h3&gt;
&lt;p&gt;Redirect all http traffic which has no other better matching server_name defined from &lt;a href=&#34;http://www.domain.tld&#34;&gt;www.domain.tld&lt;/a&gt; and domain.tld to &lt;a href=&#34;https://domain.tld&#34;&gt;https://domain.tld&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# redirect http://www to https non www                                                                               
# global and maybe not the best in all setups
server {
   listen 80;
   server_name ~^(www\.)(?&amp;lt;domain&amp;gt;.+)$;
   return 301 https://$domain$request_uri;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;http--https&#34;&gt;http &amp;gt; https&lt;/h3&gt;
&lt;p&gt;Redirect all of the incoming http requests to https for selected domains&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;server {
        listen 80;
        server_name domain.tld www.domain.tld;
        return 301 https://$host$request_uri;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;http--https-with-exceptions&#34;&gt;http &amp;gt; https with exceptions&lt;/h3&gt;
&lt;p&gt;Redirect all or a part of the incoming http requests to https, you can use this the other way round to redirect a small part of the traffic to https and &lt;code&gt;localion / &lt;/code&gt; to the normal backend.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;server {
    listen 80;
    server_name domain.tld *.domain.tld;

    # Excluded location from https redirect
    # I use this for lets encrypt validation
    location ~ acme-challenge {
        root        /etc/letsencrypt/temp;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More will be added ;-)&lt;/p&gt;
&lt;p&gt;If you have a redirect you just can&amp;rsquo;t get to work, leave a comment so I can give it a try.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Nginx map subdomain to subfolder</title>
      <link>https://aapjeisbaas.nl/post/nginx-map-subdomain-to-subfolder/</link>
      <pubDate>Wed, 20 Sep 2017 20:56:35 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/nginx-map-subdomain-to-subfolder/</guid>
      <description>&lt;p&gt;In this tutorial I&amp;rsquo;ll show you how to direct your incoming web traffic dynamically to folders on your server.
This is really useful for a lot of setups I use this one a lot for development environments because you can give developers rights to create new folders and it automatically has its own sub domain.&lt;/p&gt;
&lt;h3 id=&#34;example&#34;&gt;Example:&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DNS endpoint&lt;/th&gt;
&lt;th&gt;Folder on disk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/example.com/htdocs/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;test.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/example.com/test/htdocs/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dev1.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/example.com/dev1/htdocs/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;nginx-config&#34;&gt;Nginx config&lt;/h3&gt;
&lt;p&gt;Place this in your server block and remove the current &lt;code&gt;server_name&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;server_name     &amp;quot;~^(?&amp;lt;subdomain&amp;gt;.+).example.com$&amp;quot;;
root            /var/www/example.com/$subdomain/htdocs;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&amp;rsquo;s all, if you have questions or suggestions, post them bellow.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Use a deafult cert for nginx</title>
      <link>https://aapjeisbaas.nl/post/use-a-deafult-cert-for-nginx/</link>
      <pubDate>Wed, 26 Apr 2017 15:11:18 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/use-a-deafult-cert-for-nginx/</guid>
      <description>&lt;p&gt;If you use a service like cloudflare or sucuri you don&amp;rsquo;t need a valid cert on the backend servers to do full ssl to backend.
If you want full ssl to the server you need 2 things&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;self signed cert&lt;/li&gt;
&lt;li&gt;nginx proxy to send traffic to port 80&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using this method you need your application to send out redirects to ssl version of pages.&lt;/p&gt;
&lt;h2 id=&#34;self-signed-crt&#34;&gt;Self signed crt&lt;/h2&gt;
&lt;p&gt;You can gererate one with:

&lt;a href=&#34;https://gitlab.com/aapjeisbaas/sysop-tools/blob/master/selfsigned.sh&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;selfsigned.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Usage: &lt;code&gt;./selfsigned.sh server.name.tld&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This creates 2 files &lt;code&gt;server.name.tld.key&lt;/code&gt; and &lt;code&gt;server.name.tld.crt&lt;/code&gt; that should be coppied to &lt;code&gt;/etc/nginx/ssl&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;nginx-server-block&#34;&gt;Nginx server block&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# /etc/nginx/conf.d/000_ssl.conf

server {
  listen 443 default_server ssl;

  ssl_certificate	/etc/nginx/ssl/server.name.tld.crt;
  ssl_certificate_key	/etc/nginx/ssl/server.name.tld.key;

  location / {
      proxy_set_header          Host $host;
      proxy_set_header          X-Forwarded-Proto $scheme;
      proxy_pass                http://localhost:80;
  }

}
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Wi-Fi based dual relay on the cheap</title>
      <link>https://aapjeisbaas.nl/post/wi-fi-based-dual-relay-on-the-cheap/</link>
      <pubDate>Tue, 28 Mar 2017 01:28:42 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/wi-fi-based-dual-relay-on-the-cheap/</guid>
      <description>&lt;p&gt;I recently discovered the 
&lt;a href=&#34;http://s.click.aliexpress.com/e/QbIynII&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ESP8266&lt;/a&gt; module, it comunicates over serial and you can flash it with custom firware like the arduino bootloader.
The module is insanely cheap and realy versatile due to the replacable boot code.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/QbIynII&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;img src=&#34;https://lh3.googleusercontent.com/PegZ8YhMAXGq5lfyA7vIVsHtOX7ybEgGu8zSVrl9CplYjhOcnCcGmt5AqFxiWSmzvN4iDJDW0LYtedq8KkIGhxNOmEI7quZ5jZlWFqibI0FJXIAv7-zx7iuZib2ZhwbP4uNPulA=w500-no-no&#34; alt=&#34;&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;nagging-about-the-old-setup&#34;&gt;Nagging about the old setup&lt;/h2&gt;
&lt;p&gt;The reason I started this project is the need for a more reliable relay connected to my 
&lt;a href=&#34;https://www.domoticz.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;domoticz&lt;/a&gt; home management system. Before this I was using 433Mhz based relays, at first just bit banging commands over to a 433Mhz transmitter and later with a 
&lt;a href=&#34;http://www.rfxcom.com&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;RFXtrx433E&lt;/a&gt;.
When I got any of these to work I was just relieved it did as expected and pretty happy with the results. After a while though the cracks started to show, everything has a noticeable delay and you cant send out commands too fast after each other or recievers will simply not react.&lt;/p&gt;
&lt;h2 id=&#34;solution-time&#34;&gt;Solution Time&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;MQTT&lt;/li&gt;
&lt;li&gt;Wi-Fi&lt;/li&gt;
&lt;li&gt;small&lt;/li&gt;
&lt;li&gt;cheap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I did a bunch of other projects based around MQTT pub sub systems and know for a fact if is fast, reliable and posible to run on a embedded devices.&lt;/p&gt;
&lt;p&gt;I found a open source project that uses those techniques together with domoticz or openhab.&lt;/p&gt;
&lt;p&gt;
&lt;a href=&#34;https://www.letscontrolit.com/wiki/index.php/ESPEasy&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;img src=&#34;https://www.letscontrolit.com/wiki/resources/assets/lets.png&#34; alt=&#34;&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;later-more-on-software-and-config&#34;&gt;Later more on software and config&lt;/h3&gt;
&lt;h2 id=&#34;my-first-home-made-wifi-based-multi-relay&#34;&gt;My first home made wifi based multi relay&lt;/h2&gt;
&lt;p&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/QbIynII&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;img src=&#34;https://lh3.googleusercontent.com/SzTdsr3tFgjNwD27AaLjpT2LRlX-8b7wr9zCopxXMqbQ_Mr12vT1sAnmJL0Ror7DN4odLDTv_CYuNbvq3BPwP2GbIZhAwSIsQx6VwZYg2rHVgsH9xs4_OLmhXn22tolRE8KCd3w=w500-no-no&#34; alt=&#34;&#34;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;partlist&#34;&gt;Partlist&lt;/h2&gt;
&lt;p&gt;In the last part of this spost I&amp;rsquo;ll list some of the hardware items used to create this device.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;Function&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;Qty needed&lt;/th&gt;
&lt;th style=&#34;text-align:center&#34;&gt;Cheapest offer&lt;/th&gt;
&lt;th style=&#34;text-align:right&#34;&gt;Seen at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ESP8266&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;Wi-Fi + microcontroler&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;1&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;€1.69&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/QbIynII&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Aliexpress&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breakout board&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;2mm -&amp;gt; 2.54mm pins&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;1&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;€0.16&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/auvRvfM&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Aliexpress&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.54mm stackable pin headers&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;Socket for the ESP&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;4x 8pins&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;€0.24&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/MBiMbuN&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Aliexpress&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRD-05VDC-SL-C T73-5V 10A&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;High voltage relay&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;2&lt;/td&gt;
&lt;td style=&#34;text-align:center&#34;&gt;€0.60&lt;/td&gt;
&lt;td style=&#34;text-align:right&#34;&gt;
&lt;a href=&#34;http://s.click.aliexpress.com/e/6EaAMfU&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Aliexpress&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;more-to-come-this-page-will-be-updated-in-the-future&#34;&gt;more to come this page will be updated in the future&lt;/h3&gt;
</description>
    </item>
    
    <item>
      <title>Update forked git repo from original source</title>
      <link>https://aapjeisbaas.nl/post/update-forked-git-repo-from-original-source/</link>
      <pubDate>Wed, 22 Mar 2017 09:41:49 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/update-forked-git-repo-from-original-source/</guid>
      <description>&lt;h3 id=&#34;1-clone-your-fork&#34;&gt;1. Clone your fork:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git clone git@github.com:YOUR-USERNAME/YOUR-FORKED-REPO.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;2-add-remote-from-original-repository-in-your-forked-repository&#34;&gt;2. Add remote from original repository in your forked repository:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cd into/cloned/fork-repo
git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git
git fetch upstream
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&#34;3-updating-your-fork-from-original-repo-to-keep-up-with-their-changes&#34;&gt;3. Updating your fork from original repo to keep up with their changes:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git pull upstream master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;
&lt;a href=&#34;https://gist.github.com/CristinaSolana/1885435&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Original post&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Map client header to upstream in nginx</title>
      <link>https://aapjeisbaas.nl/post/map-client-header-to-upstream-in-nginx/</link>
      <pubDate>Mon, 20 Mar 2017 12:00:22 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/map-client-header-to-upstream-in-nginx/</guid>
      <description>&lt;p&gt;Sometimes you want to route traffic to different endpoints based on a http header.&lt;/p&gt;
&lt;p&gt;Avoiding if statements in the nginx config is the most important part here.&lt;/p&gt;
&lt;p&gt;The example below is based on the user-agent header that is send from the client.&lt;/p&gt;
&lt;h2 id=&#34;test-nginx-conf-to-view-traffic-flow&#34;&gt;Test nginx conf to view traffic flow&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# create upstreams
# android and other non ios device
upstream android {
     server 127.0.0.1:8080;
}
# ios based devices
upstream ios {
     server 127.0.0.1:8081;
}

# map to different upstream backends based on user-agent header
map $http_user_agent $mobile_upstream {
     default    &amp;quot;android&amp;quot;;
     ~iPod      &amp;quot;ios&amp;quot;;
     ~iPad      &amp;quot;ios&amp;quot;;
     ~iOS       &amp;quot;ios&amp;quot;;
     ~iPhone    &amp;quot;ios&amp;quot;;
}

server {
    listen 80;
    access_log off;
    location / {
        proxy_pass http://$mobile_upstream;
    }
}
server {
    listen 8080;
    access_log /var/log/nginx/default-android.log combined;

}
server {
    listen 8081;
    access_log /var/log/nginx/android-ios.log combined;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;another-example-when-you-already-have-if-in-your-upstream-config-inside-you-server-block&#34;&gt;Another example, when you already have if in your upstream config inside you server{} block&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;
# map function needs to before the server{} block 
# I use:  /etc/nginx/conf.d/00-map-mobile.conf
# set $mobile_device to 0 or 1 
map $http_user_agent $mobile_device {
  default      0;
  ~iPod        1;
  ~iPad        1;
  ~iOS 1;
  ~iPhone      1;
  ~mobile      1;
  ~Android     1;
}

# In the server{} block
include /etc/nginx/includes/ab-upstreams.conf
proxy_pass http://$upstream;

# Inside ab-upstreams.conf
# stuur naar nieuwe locatie       
if ($http_cookie ~ &amp;quot;version=new&amp;quot;) {     
        set $upstream &amp;quot;new_cluster&amp;quot;;      
} 
if ($request ~ &amp;quot;version=new&amp;quot;) { 
        set $upstream &amp;quot;new_cluster&amp;quot;;      
} 
  
# stuur user terug naar oude locatie      
if ($http_cookie ~ &amp;quot;version=old&amp;quot;) {     
        set $upstream &amp;quot;old_cluster&amp;quot;;        
} 
if ($request ~ &amp;quot;version=old&amp;quot;) { 
        set $upstream &amp;quot;old_cluster&amp;quot;;      
} 
  
# force modbile naar oude upstreams       
if ($mobile_device = 1) { 
        set $upstream &amp;quot;old_cluster&amp;quot;;      
}



&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Push ssh public key to lxc container</title>
      <link>https://aapjeisbaas.nl/post/push-ssh-public-key-to-lxc-container/</link>
      <pubDate>Mon, 20 Mar 2017 10:03:42 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/push-ssh-public-key-to-lxc-container/</guid>
      <description>&lt;p&gt;LXC is by far my favorite virtualization software for the desktop. I can spin up a base image to test something within seconds.&lt;/p&gt;
&lt;p&gt;After starting there is one thing I&amp;rsquo;ll always do: setup SSH key login, then it feels like any other machine to manage.&lt;/p&gt;
&lt;p&gt;Lets go trough the steps.&lt;/p&gt;
&lt;h2 id=&#34;setup-a-test-machine&#34;&gt;Setup a test machine&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;svanbroekhoven@PC-stein:~$ lxc image list
+-------+--------------+--------+-------------------------------------------+--------+----------+------------------------------+
| ALIAS | FINGERPRINT  | PUBLIC |                DESCRIPTION                |  ARCH  |   SIZE   |         UPLOAD DATE          |
+-------+--------------+--------+-------------------------------------------+--------+----------+------------------------------+
|       | 70223d3f415c | no     | ubuntu 16.04 LTS amd64 (daily) (20170317) | x86_64 | 145.34MB | Mar 18, 2017 at 4:44am (UTC) |
+-------+--------------+--------+-------------------------------------------+--------+----------+------------------------------+
svanbroekhoven@PC-stein:~$ lxc launch ubuntu:16.04 nginx-test
Creating nginx-test
Starting nginx-test          
svanbroekhoven@PC-stein:~$ lxc list
+----------------+---------+----------------------+-----------------------------------------------+------------+-----------+
|      NAME      |  STATE  |         IPV4         |                     IPV6                      |    TYPE    | SNAPSHOTS |
+----------------+---------+----------------------+-----------------------------------------------+------------+-----------+
|   nginx-test   | RUNNING | 10.87.183.245 (eth0) | fd99:3066:2539:9f7d:216:3eff:feef:cefd (eth0) | PERSISTENT | 0         |
+----------------+---------+----------------------+-----------------------------------------------+------------+-----------+
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;transfer-ssh-public-key&#34;&gt;Transfer SSH public key&lt;/h2&gt;
&lt;p&gt;Now we have a machine running and are ready to push our SSH key into the container.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Transfer the public key
svanbroekhoven@PC-stein:~$ lxc file push ~/.ssh/id_rsa.pub nginx-test/root/.ssh/authorized_keys  

# Open up a bash shell (ip from the lxc list)
svanbroekhoven@PC-stein:~$ lxc exec nginx-test bash
root@nginx-test:~# ll
total 20
drwx------  3 root root 4096 Mar 20 08:37 ./
drwxr-xr-x 22 root root 4096 Mar  8 02:20 ../
-rw-r--r--  1 root root 3106 Oct 22  2015 .bashrc
-rw-r--r--  1 root root  148 Aug 17  2015 .profile
drwx------  2 root root 4096 Mar 20 08:37 .ssh/

# Edit the file permissions
root@nginx-test:~# chmod 600 /root/.ssh/authorized_keys &amp;amp;&amp;amp; sudo chown root: /root/.ssh/authorized_keys
root@nginx-test:~# exit

# Test the connection
svanbroekhoven@PC-stein:~$ ssh root@10.87.183.245
The authenticity of host &#39;10.87.183.245 (10.87.183.245)&#39; can&#39;t be established.
ECDSA key fingerprint is SHA256:LA31gxLK9zhA7qMaGwpNNKXcYb+9eht5FPvNwRUM/cE.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added &#39;10.87.183.245&#39; (ECDSA) to the list of known hosts.

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

root@nginx-test:~# logout
Connection to 10.87.183.245 closed.
svanbroekhoven@PC-stein:~$ 
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Comodo PositiveSSL CA Chain</title>
      <link>https://aapjeisbaas.nl/post/comodo-positivessl-ca-chain/</link>
      <pubDate>Fri, 17 Mar 2017 15:04:01 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/comodo-positivessl-ca-chain/</guid>
      <description>&lt;p&gt;This is the current PositiveSSL ca chain.&lt;/p&gt;
&lt;p&gt;Apache and Nginx seem to have different implementations on the chain order these are the right orders for PositiveSSL.&lt;/p&gt;
&lt;h2 id=&#34;nginx&#34;&gt;Nginx&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
+AZxAeKCINT+b72x
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
pu/xO28QOG8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;apache&#34;&gt;Apache&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
pu/xO28QOG8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
+AZxAeKCINT+b72x
-----END CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Centos auto start service</title>
      <link>https://aapjeisbaas.nl/post/centos-auto-start-service/</link>
      <pubDate>Fri, 17 Mar 2017 13:37:09 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/centos-auto-start-service/</guid>
      <description>&lt;p&gt;There are many ways to accomplish the same result this is just my preference.&lt;/p&gt;
&lt;h2 id=&#34;centos-6&#34;&gt;Centos 6&lt;/h2&gt;
&lt;p&gt;To auto start services in Centos or Redhat OS, you can use builtin chkconfig utility.
It is located in /sbin directory.
If you are a regular user (non-root), then /sbin may not be in your path.
Therefore, you may have to use the full path to access the chkconfig utility.&lt;/p&gt;
&lt;p&gt;To auto start a new service:
Find out the name of service script from /etc/init.d/ directory e.g. mysqld or httpd Add it to chkconfig&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /sbin/chkconfig --add mysqld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure it is in the chkconfig.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /sbin/chkconfig --list mysqld
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set it to autostart&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /sbin/chkconfig mysqld on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To stop a service from auto starting on boot&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo /sbin/chkconfig mysqld off
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&#34;centos-7&#34;&gt;Centos 7&lt;/h2&gt;
&lt;p&gt;When a service you installed needs to be started at boot use the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;sudo systemctl enable nginx.service
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Apache force www subdomain</title>
      <link>https://aapjeisbaas.nl/post/apache-force-www-subdomain/</link>
      <pubDate>Fri, 17 Mar 2017 13:25:23 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/apache-force-www-subdomain/</guid>
      <description>&lt;p&gt;A redirect in an apache vhost to force the website to www.$domain.tld&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;

...

RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=302,L]

&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;
# test and reload
apachectl -t &amp;amp;&amp;amp; apachectl -k graceful

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Backup all databases on a plesk server</title>
      <link>https://aapjeisbaas.nl/post/backup-all-databases-on-a-plesk-server/</link>
      <pubDate>Fri, 12 Aug 2016 10:55:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/backup-all-databases-on-a-plesk-server/</guid>
      <description>&lt;p&gt;This is not the fastest not the safest and not the most eficient, but it does work. and unless you have a realy bussy database this is a pretty oke way to backup your database.
It uses mysqldump and the internal plesk credentials so no need to type passwords at all.&lt;/p&gt;
&lt;p&gt;It can ofcourse be used on any other mysql server as long as you set the right credentials.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#! /bin/bash
# aapjeisbaas.nl
# 2016-08-12
 
TIMESTAMP=$(date +&amp;quot;%F&amp;quot;)
BACKUP_DIR=&amp;quot;/tmp/db-dump/$TIMESTAMP&amp;quot;
MYSQL_USER=&amp;quot;admin&amp;quot;
MYSQL=/usr/bin/mysql
MYSQL_PASSWORD=&amp;quot;$(cat /etc/psa/.psa.shadow)&amp;quot;
MYSQLDUMP=/usr/bin/mysqldump 

mkdir -p &amp;quot;$BACKUP_DIR&amp;quot;

databases=$($MYSQL --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;SHOW DATABASES;&amp;quot; | grep -Ev &amp;quot;(Database|information_schema|performance_schema)&amp;quot;)
for db in $databases; do
  $MYSQLDUMP --force --opt --user=$MYSQL_USER -p$MYSQL_PASSWORD --databases $db | gzip &amp;gt; &amp;quot;$BACKUP_DIR/$db.sql.gz&amp;quot;
done

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Put this in a file: &lt;code&gt;vim backup-db.sh&lt;/code&gt; make it executable: &lt;code&gt;chmod +x backup-db.sh&lt;/code&gt; and run it &lt;code&gt;./backup-db.sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will take some time, in some cases 15 minutes or even more.
It will backup to: &lt;code&gt;/tmp/db-dump/$TIMESTAMP&lt;/code&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Keep nginx from pushing backend port on try and redirects</title>
      <link>https://aapjeisbaas.nl/post/keep-nginx-from-pushing-backend-port-on-try-and-redirects/</link>
      <pubDate>Fri, 05 Aug 2016 12:10:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/keep-nginx-from-pushing-backend-port-on-try-and-redirects/</guid>
      <description>&lt;p&gt;Ever found yourself wondering why your nginx setup is returning a port from the backend vhost?&lt;/p&gt;
&lt;p&gt;QUICK FIX: &lt;code&gt;port_in_redirect off;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I found some tweaks to prevent this from happening, first a example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# This is the result of a: try $uri $uri/ in a backend vhost
stein@stein ~ $ curl --head http://www.example.nl/test
HTTP/1.1 301 Moved Permanently
Server: nginx
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.example.nl:8080/test/
Accept-Ranges: bytes
X-Varnish: 1044342160
Age: 0
Via: 1.1 varnish
Vary: User-Agent
X-Cache: MISS
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The nginx config I used before ifound out what was wrong is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
        listen                          80;
        server_name                     *.example.nl example.nl;

        root                            /var/www/vhosts/example.nl/httpdocs;
        include                         includes/default_vhost.conf; # proxy timeouts etc. nothing interesting

        location / {
                include                         includes/proxy_varnish.conf; 	# location with varnish related conf
	}									# varnish sends requests on to 8080 if not in cache
}
server {
        listen                          8080;
        server_name                     *.example.nl example.nl;

        root                            /var/www/vhosts/example.nl/httpdocs;
        index                           index.php index.html index.htm;

        location / {
                index                           index.php;
                try_files                       $uri $uri/ @handler;
        }

        location ~ .php/ {
                rewrite                         ^(.*.php)/ $1 last;
        }

        location @handler {
                rewrite                         / /index.php;
        }

        location ~ \.php/ {
                rewrite ^(.*\.php)/ $1 last;
        }

        include includes/hhvm.conf; # Sends fascgi to hhvm
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It frustrated me that the solution is not that well documented but here it is: &lt;code&gt;port_in_redirect off;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Yes, noting exiting here, just place &lt;code&gt;port_in_redirect off;&lt;/code&gt; in the backend vhost. There is also a way to not send the full hostname: &lt;code&gt;server_name_in_redirect off;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will result in a config like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
        listen                          80;
        server_name                     *.example.nl example.nl;

        root                            /var/www/vhosts/example.nl/httpdocs;
        include                         includes/default_vhost.conf; # proxy timeouts etc. nothing interesting

        location / {
                include                         includes/proxy_varnish.conf; 	# location with varnish related conf
	}									# varnish sends requests on to 8080 if not in cache
}
server {
        listen                          8080;
        server_name                     *.example.nl example.nl;

        root                            /var/www/vhosts/example.nl/httpdocs;
        index                           index.php index.html index.htm;
	port_in_redirect		off; 

        location / {
                index                           index.php;
                try_files                       $uri $uri/ @handler;
        }

        location ~ .php/ {
                rewrite                         ^(.*.php)/ $1 last;
        }

        location @handler {
                rewrite                         / /index.php;
        }

        location ~ \.php/ {
                rewrite ^(.*\.php)/ $1 last;
        }

        include includes/hhvm.conf; # Sends fascgi to hhvm
}

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Connect chromebook to openvpn with custom routes</title>
      <link>https://aapjeisbaas.nl/post/connect-chromebook-to-openvpn-with-custom-routes/</link>
      <pubDate>Sat, 23 Jul 2016 19:17:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/connect-chromebook-to-openvpn-with-custom-routes/</guid>
      <description>&lt;p&gt;This post will try to help you get a openvpn connection on a chromebook with a pushed route instead of a redirected gateway.
By default chromeos redirects all traffic to the vpn.
To overcome this you can make a onc file that can use all the ovpn config options instead of the very limited gui options.&lt;/p&gt;
&lt;p&gt;To start you will need the following files:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ca.crt
client.key
client.crt
client.ovpn (or equivalent)
# user and pass auth enabled in server (auth-user-pass)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the end you will have 3 files that will allow you to connect to the vpn.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;ca.crt
client.p12
chrome-vpn.onc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First we create the &lt;code&gt;p12&lt;/code&gt; file to be able to import the user cert and key combo into the chromeos device.
This can be done on a external system or in a shell on the chromebook in developer mode.
Puting your device in developer mode is not necessary if you have a separate machine to create the. &lt;code&gt;p12&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# After running this command you will be asked to set a password for the p12.
# You will need this password to read the contents of it. (when you import it)
openssl pkcs12 -export -in client.crt -inkey client.key -certfile ca.crt -name ChromeBook -out client.p12
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Import the root CA user p12 on your device&lt;/p&gt;
&lt;p&gt;Go to the folowing URL on your chromebook:
&lt;code&gt;chrome://settings/certificates&lt;/code&gt;
Click on the Authorities tab
Import the &lt;code&gt;ca.crt&lt;/code&gt; and select &lt;code&gt;&amp;quot;Trust this certificate for identifiying websites.&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Go to the first tab in the certificate manager (Your certificates)
Click on &amp;ldquo;Import and bind to device&amp;rdquo;
Select client.p12
Click on OK and a password window will appear.
Input the password you chose earlier when you created the &lt;code&gt;p12&lt;/code&gt; file&lt;/p&gt;
&lt;p&gt;That was the easy part, now for creating the onc file. Underneath is an example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
 &amp;quot;Type&amp;quot;:&amp;quot;UnencryptedConfiguration&amp;quot;,
      &amp;quot;Certificates&amp;quot;: [ {
      &amp;quot;GUID&amp;quot;: &amp;quot;{&amp;lt;GUID#1&amp;gt;}&amp;quot;,
      &amp;quot;Type&amp;quot;: &amp;quot;Authority&amp;quot;,
      &amp;quot;X509&amp;quot;: &amp;quot;&amp;lt;CA_CERT&amp;gt;&amp;quot;
   } ],
    &amp;quot;NetworkConfigurations&amp;quot;: [ {
      &amp;quot;GUID&amp;quot;: &amp;quot;{&amp;lt;GUID#2&amp;gt;}&amp;quot;,
      &amp;quot;Name&amp;quot;: &amp;quot;&amp;lt;VPN_NAME&amp;gt;&amp;quot;,
      &amp;quot;Type&amp;quot;: &amp;quot;VPN&amp;quot;,
      &amp;quot;VPN&amp;quot;: {
          &amp;quot;Type&amp;quot;: &amp;quot;OpenVPN&amp;quot;,
          &amp;quot;Host&amp;quot;: &amp;quot;&amp;lt;HOSTHAME&amp;gt;&amp;quot;,
          &amp;quot;OpenVPN&amp;quot;: {
                        &amp;quot;ServerCARef&amp;quot;: &amp;quot;{&amp;lt;GUID#1&amp;gt;}&amp;quot;,
                    &amp;quot;AuthRetry&amp;quot;: &amp;quot;interact&amp;quot;,
                    &amp;quot;ClientCertType&amp;quot;: &amp;quot;Pattern&amp;quot;,
                    &amp;quot;ClientCertPattern&amp;quot;: {              
                          &amp;quot;IssuerCARef&amp;quot;: [ &amp;quot;{&amp;lt;GUID#1&amp;gt;}&amp;quot; ]
                         },
                    &amp;quot;CompLZO&amp;quot;: &amp;quot;true&amp;quot;,
                    &amp;quot;Port&amp;quot;: 1194,
                    &amp;quot;Proto&amp;quot;: &amp;quot;udp&amp;quot;,
                    &amp;quot;RemoteCertTLS&amp;quot;:&amp;quot;server&amp;quot;,
                    &amp;quot;RemoteCertEKU&amp;quot;: &amp;quot;TLS Web Server Authentication&amp;quot;,
                    &amp;quot;SaveCredentials&amp;quot;: false,
                    &amp;quot;ServerPollTimeout&amp;quot;: 10,
                    &amp;quot;Username&amp;quot;: &amp;quot;&amp;lt;USERNAME&amp;gt;&amp;quot;,
                    &amp;quot;KeyDirection&amp;quot;:&amp;quot;1&amp;quot;,                    
                    &amp;quot;TLSAuthContents&amp;quot;:&amp;quot;&amp;lt;TLS_AUTH_KEY&amp;gt;&amp;quot;
                     }
             }
                               } ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In there we first replace the &lt;code&gt;&amp;lt;GUID&amp;gt;&lt;/code&gt; with a guid from:
&lt;code&gt;https://www.uuidgenerator.net/&lt;/code&gt;
Replace all the &lt;code&gt;&amp;lt;GUID#1&amp;gt;&lt;/code&gt; with the same guid, use another one for the &lt;code&gt;&amp;lt;GUID#2&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Change the &lt;code&gt;&amp;lt;CA_CERT&amp;gt;&lt;/code&gt; with the content of the &lt;code&gt;ca.crt&lt;/code&gt; file without: &lt;code&gt; -----BEGIN CERTIFICATE----- and -----END CERTIFICATE-----&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# ca.crt:
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----


#becomes one line without begin and end:
&amp;quot;X509&amp;quot;: &amp;quot;MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOF.....KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==&amp;quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rename the VPN connection, this is how will sea it from the user perspective.
Chenge &lt;code&gt;&amp;lt;VPN_NAME&amp;gt;&lt;/code&gt; to whatever you want.&lt;/p&gt;
&lt;p&gt;Now We get to the actual OpenVPN config&lt;/p&gt;
&lt;p&gt;Edit the folowing: &lt;code&gt;&amp;lt;HOSTHAME&amp;gt;&lt;/code&gt; to your vpn server hostname or ip&lt;/p&gt;
&lt;p&gt;Make sure that these are the same uuid as the one in the ca crt block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;ServerCARef&amp;quot;: &amp;quot;{&amp;lt;GUID#1&amp;gt;}&amp;quot;,
&amp;quot;IssuerCARef&amp;quot;: [ &amp;quot;{&amp;lt;GUID#1&amp;gt;}&amp;quot; ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we have some options:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;CompLZO&amp;quot;: &amp;quot;true&amp;quot;,
&amp;quot;Port&amp;quot;: 1194,
&amp;quot;Proto&amp;quot;: &amp;quot;udp&amp;quot;,
&amp;quot;RemoteCertTLS&amp;quot;:&amp;quot;server&amp;quot;,
&amp;quot;RemoteCertEKU&amp;quot;: &amp;quot;TLS Web Server Authentication&amp;quot;,
&amp;quot;SaveCredentials&amp;quot;: true,
&amp;quot;ServerPollTimeout&amp;quot;: 10,
&amp;quot;Username&amp;quot;: &amp;quot;&amp;lt;USERNAME&amp;gt;&amp;quot;,
&amp;quot;KeyDirection&amp;quot;:&amp;quot;1&amp;quot;,                    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can add a password with:
&lt;code&gt;&amp;quot;Password&amp;quot;: &amp;quot;supersectretpass&amp;quot;,&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Remove or add whatever you need, you can find the full spec here 
&lt;a href=&#34;https://src.chromium.org/viewvc/chrome/trunk/src/components/onc/docs/onc_spec.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ONC spec&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you use tls auth with your vpn there is a catch in making te config.
You need to strip the # comment lines and place &lt;code&gt;\n&lt;/code&gt; in the place of newlines.
Here is what is should look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;TLSAuthContents&amp;quot;:&amp;quot;-----BEGIN OpenVPN Static key V1-----\n1f2537daea1be955f7...ef396b1c5df8f5a8c\n-----END OpenVPN Static key V1-----\n&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now for the option that makes youable to only use pushed routes from the vpn server instead of redirecting everything:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;IgnoreDefaultRoute&amp;quot;: true,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Put that in the openvpn block to disable the automatic redirection of all your web traffic.
Here is my Office onc file with all the &amp;ldquo;juicy&amp;rdquo; details removed.&lt;/p&gt;
&lt;p&gt;You can save the file and import it here: &lt;code&gt;chrome://net-internals/#chromeos&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{
 &amp;quot;Type&amp;quot;:&amp;quot;UnencryptedConfiguration&amp;quot;,
      &amp;quot;Certificates&amp;quot;: [ {
      &amp;quot;GUID&amp;quot;: &amp;quot;{90a39a1e-2ce8-4fc9-b824-0e1a4faad3e5}&amp;quot;,
      &amp;quot;Type&amp;quot;: &amp;quot;Authority&amp;quot;,
      &amp;quot;X509&amp;quot;: &amp;quot;MIIEMjCCAxqgAw......eNDl6nIw==&amp;quot;
   } ],
    &amp;quot;NetworkConfigurations&amp;quot;: [ {
      &amp;quot;GUID&amp;quot;: &amp;quot;{c44e6e82-c0c2-44e8-9f1c-579404464dc4}&amp;quot;,
      &amp;quot;Name&amp;quot;: &amp;quot;Office VPN&amp;quot;,
      &amp;quot;Type&amp;quot;: &amp;quot;VPN&amp;quot;,
      &amp;quot;VPN&amp;quot;: {
          &amp;quot;Type&amp;quot;: &amp;quot;OpenVPN&amp;quot;,
          &amp;quot;Host&amp;quot;: &amp;quot;office.domain.tld&amp;quot;,
          &amp;quot;OpenVPN&amp;quot;: {
                        &amp;quot;ServerCARef&amp;quot;: &amp;quot;{90a39a1e-2ce8-4fc9-b824-0e1a4faad3e5}&amp;quot;,
                    &amp;quot;AuthRetry&amp;quot;: &amp;quot;interact&amp;quot;,
                    &amp;quot;ClientCertType&amp;quot;: &amp;quot;Pattern&amp;quot;,
                    &amp;quot;ClientCertPattern&amp;quot;: {              
                          &amp;quot;IssuerCARef&amp;quot;: [ &amp;quot;{90a39a1e-2ce8-4fc9-b824-0e1a4faad3e5}&amp;quot; ]
                         },
                    &amp;quot;Port&amp;quot;: 1194,
                    &amp;quot;Proto&amp;quot;: &amp;quot;udp&amp;quot;,
                    &amp;quot;Cipher&amp;quot;: &amp;quot;AES-256-CBC&amp;quot;,
                    &amp;quot;CompLZO&amp;quot;: &amp;quot;true&amp;quot;,
                    &amp;quot;IgnoreDefaultRoute&amp;quot;: true,
                    &amp;quot;RemoteCertTLS&amp;quot;:&amp;quot;server&amp;quot;,
                    &amp;quot;SaveCredentials&amp;quot;: true,
                    &amp;quot;ServerPollTimeout&amp;quot;: 10,
                    &amp;quot;Username&amp;quot;: &amp;quot;user&amp;quot;,
                    &amp;quot;Password&amp;quot;: &amp;quot;secretpass&amp;quot;,
                    &amp;quot;KeyDirection&amp;quot;:&amp;quot;1&amp;quot;,                    
                    &amp;quot;TLSAuthContents&amp;quot;:&amp;quot;-----BEGIN OpenVPN Static key V1-----\n1f2537da....a8c\n-----END OpenVPN Static key V1-----\n&amp;quot;
                     }
             }
                               } ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more info check the folowing links:&lt;/p&gt;
&lt;p&gt;
&lt;a href=&#34;https://src.chromium.org/viewvc/chrome/trunk/src/components/onc/docs/onc_spec.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ONC Spec&lt;/a&gt;

&lt;a href=&#34;https://goo.gl/bp5lho&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ChromeOS VPN ONC block example&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Auto update wordpress</title>
      <link>https://aapjeisbaas.nl/post/auto-update-wordpress/</link>
      <pubDate>Fri, 22 Jul 2016 16:41:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/auto-update-wordpress/</guid>
      <description>&lt;p&gt;Neat trick to always auto update wordpress and plugins.&lt;/p&gt;
&lt;p&gt;Pro Tip: You should make regular backups to have something to go back to if the update f*d your site ;-)&lt;/p&gt;
&lt;p&gt;First we need to change the permissions and ownership of the web dir.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Change Owner and Group to SomeUser and WebServerGroup (ftp-user:www-data)
chown -R ftp-user:www-data /path/to/wp
# Change folders to 775 
find /path/to/wp -type d -exec chmod 775 {} \; 
# files op 664 
find /path/to/wp -type f -exec chmod 664 {} \;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to set the prefered file access method for wordpress.&lt;/p&gt;
&lt;p&gt;Open the wp-config.php file and add the following lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;define(&#39;FS_METHOD&#39;, &#39;direct&#39;);
define( &#39;WP_AUTO_UPDATE_CORE&#39;, true );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This update uses a MU plugin (Must Use plugin)&lt;/p&gt;
&lt;p&gt;This can be achiefed by creating the following directory:
&lt;code&gt;wp-content/mu-plugins&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;All php files in this dir will allways be used by wordpress.&lt;/p&gt;
&lt;p&gt;Now create &lt;code&gt;wp-content/mu-plugins/updates.php&lt;/code&gt; and add the following content:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-php&#34;&gt;add_filter( &#39;allow_dev_auto_core_updates&#39;, &#39;__return_false&#39; ); 
add_filter( &#39;auto_update_plugin&#39;, &#39;__return_true&#39; ); 
add_filter( &#39;auto_update_theme&#39;, &#39;__return_true&#39; );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your site will stay up to date ;-)&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Remote diff over ssh</title>
      <link>https://aapjeisbaas.nl/post/remote-diff-over-ssh/</link>
      <pubDate>Wed, 13 Jul 2016 14:00:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/remote-diff-over-ssh/</guid>
      <description>&lt;p&gt;Want to do a diff on a local and remote file, or on 2 remote files.&lt;/p&gt;
&lt;p&gt;Here is a small trick&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Local and remote diff
 diff file &amp;lt;(ssh remote &#39;cat file&#39;)

# Or both are on remote servers
diff &amp;lt;(ssh remote1 &#39;cat file&#39;) &amp;lt;(ssh remote2 &#39;cat file&#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use this a lot when a ansible &amp;ndash;check run tells me there is a diff and want to know what I will be overwriting.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Run a script when load is too high</title>
      <link>https://aapjeisbaas.nl/post/run-a-script-when-load-is-too-high/</link>
      <pubDate>Mon, 04 Jul 2016 10:00:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/run-a-script-when-load-is-too-high/</guid>
      <description>&lt;p&gt;This is a small script that can check if server load is too high and start another script.&lt;/p&gt;
&lt;p&gt;It is NOT best practice to fix a problem like this but it can get you trough the nights while developers work on the code problem.
I called my script &lt;code&gt;/usr/local/bin/damnnastycheck.sh&lt;/code&gt; it runs every minute from /etc/crontab yhe only requirement is BashCalc (bc)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
high=8.1

if [ $(echo $(cat /proc/loadavg | awk &#39;{print $1}&#39;)&#39;&amp;gt;&#39;$high | bc -l) == &amp;quot;1&amp;quot;  ]; then
  echo &amp;quot;high load, running fix&amp;quot;
  /usr/local/bin/damnnastyfix.sh
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the damnnastyfix you could place something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash
killall -9 httpd
service httpd restart
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Make iptables like your ftp sessions</title>
      <link>https://aapjeisbaas.nl/post/make-iptables-like-your-ftp-sessions/</link>
      <pubDate>Wed, 06 Apr 2016 23:10:00 +0200</pubDate>
      <guid>https://aapjeisbaas.nl/post/make-iptables-like-your-ftp-sessions/</guid>
      <description>&lt;p&gt;If you manage your firewall by hand have issues with connection trough ftp, try adding this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# FTP Helper (beginning of script)
modprobe ip_conntrack_ftp
modprobe ip_nat_ftp ports=21

# ... 
# other rules
# ...

# FTP Helper
iptables -A OUTPUT -o eth0 -p tcp --sport ftp -j ACCEPT
iptables -A OUTPUT -o eth0 -p tcp --sport ftp-data -j ACCEPT

# Drop rule 

#(end of script)

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Find broken RAM DIMM in server</title>
      <link>https://aapjeisbaas.nl/post/find-broken-ram-dimm-in-server/</link>
      <pubDate>Mon, 14 Mar 2016 15:10:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/find-broken-ram-dimm-in-server/</guid>
      <description>&lt;p&gt;A broken ram module is pretty annoying in itself but trying to find it by trial and error is even worse.
This simple command shows the locations of the active dimms in the system.
Run it and find the missing one, in this case:&lt;/p&gt;
&lt;p&gt;CPU#0Channel#0_DIMM#1&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[root@machine ~]# cat /sys/devices/system/edac/mc/mc*/csrow*/ch*_dimm_label 
CPU#0Channel#0_DIMM#0
CPU#0Channel#1_DIMM#0
CPU#0Channel#2_DIMM#0
CPU#0Channel#1_DIMM#1
CPU#0Channel#2_DIMM#1
CPU#1Channel#0_DIMM#0
CPU#1Channel#1_DIMM#0
CPU#1Channel#2_DIMM#0
CPU#1Channel#0_DIMM#1
CPU#1Channel#1_DIMM#1
CPU#1Channel#2_DIMM#1
[root@machine ~]# 

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;[root@machine ~]# cat /sys/devices/system/edac/mc/mc*/dimm*/dimm_label
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Block tor traffic in cloudflare firewall</title>
      <link>https://aapjeisbaas.nl/post/block-tor-traffic-in-cloudflare-firewall/</link>
      <pubDate>Wed, 09 Mar 2016 18:00:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/block-tor-traffic-in-cloudflare-firewall/</guid>
      <description>&lt;p&gt;![tor logo] ({filename}/static/images/tor.png)&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t want to allow access to your server through the tor network you can ask nicely or just add every malicious looking client to a list.
I tried to come up with a better solution.&lt;/p&gt;
&lt;p&gt;I started with a script that blocked incomming connections on a loadbalancer (can also be used on a webserver)
Then I realized that if you use cloudclare in front of that the tcp connections come from cloudare and not from the tor endpoints.
So I needed to rebuild it to add the rules to the cloudflrae firewall.&lt;/p&gt;
&lt;p&gt;I started of the same way, create a variable which contains all the tor ip adresses. Then It get&amp;rsquo;s a bit more complex, working with a json api and bash is often not without struggle.
The code is commented, if that is not enough contact me and I will try to make i more readable.
Use it, cron it, love it ;-)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;
#!/bin/bash

echo &amp;quot;Tor endpoint list loading&amp;quot;
TORLIST=$(curl -s https://check.torproject.org/exit-addresses |grep ExitAddress | awk &#39;{print $2 }&#39; | sort | uniq)

ZONE=&amp;quot;&amp;quot;   # can be left empty this script will pick the first one from your account for you
KEY=&amp;quot;&amp;quot;    # API key can be found at: https://www.cloudflare.com/a/account/my-account
EMAIL=&amp;quot;&amp;quot;  # email adress used to login to cloudflare

# If the ZONE is not defined get the first one from the api
if [ -z &amp;quot;$ZONE&amp;quot; ]; then
  ZONE=$( curl -s -X GET &amp;quot;https://api.cloudflare.com/client/v4/zones/&amp;quot; \
  -H &amp;quot;Content-Type:application/json&amp;quot; \
  -H &amp;quot;X-Auth-Key:${KEY}&amp;quot; \
  -H &amp;quot;X-Auth-Email:${EMAIL}&amp;quot; | python -m json.tool |grep -A 1 development_mode | grep id | awk &#39;{print $2}&#39; | sed &#39;s/&amp;quot;//g&#39; |sed &#39;s/,//g&#39;)
  echo &amp;quot;Add zone string to config for faster result and less api calls&amp;quot;
  echo ${ZONE}
fi

echo &amp;quot;Retrieving active firewall rules&amp;quot;
IP_LIST=&amp;quot;&amp;quot;
# The api is paged so we first qeury the api and check how many apges we have
PAGES=$(curl -s -X GET &amp;quot;https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules?mode=block&amp;amp;configuration_target=ip&amp;amp;page=1&amp;amp;per_page=1000&amp;amp;order=scope_type&amp;amp;direction=desc&amp;amp;match=all &amp;quot; \
  -H &amp;quot;Content-Type:application/json&amp;quot; \
  -H &amp;quot;X-Auth-Key:${KEY}&amp;quot; \
  -H &amp;quot;X-Auth-Email:${EMAIL}&amp;quot; | python -m json.tool |  grep  total_pages | awk &#39;{print $2}&#39;)

# for every page gat the response rinse out the bullshit keep the ip adresses
# Because we can have multiple pages I cocatinate the srings into IP_LIST
for ((page=1; $page &amp;lt;= ${PAGES}; page++)); do
  DATA=$(curl -s -X GET &amp;quot;https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules?mode=block&amp;amp;configuration_target=ip&amp;amp;page=${page}&amp;amp;per_page=1000&amp;amp;order=scope_type&amp;amp;direction=desc&amp;amp;match=all &amp;quot; \
  -H &amp;quot;Content-Type:application/json&amp;quot; \
  -H &amp;quot;X-Auth-Key:${KEY}&amp;quot; \
  -H &amp;quot;X-Auth-Email:${EMAIL}&amp;quot; | python -m json.tool |  grep value | awk &#39;{print $2}&#39;)

  IP_LIST=&amp;quot;${IP_LIST} ${DATA}&amp;quot;
done

# Now we have a list with all the tor ip exit nodes, and all the ip adresses in the firewall.
# I creatd the next loop to take all the tor endpoints and check if they are already in the firewall.
# If the tor ip is not found in the firewall list it will be added to the firewall
# There is a sleep in the loop to keep the api from blocking your reqeusts (max 1200 per 5 min.)

for ip in ${TORLIST}; do
  sleep 0.3

  IN_FIREWALL=$(echo ${IP_LIST} | grep ${ip})

  if [ -z &amp;quot;$IN_FIREWALL&amp;quot; ]; then
    echo &amp;quot;adding ${ip} to blocklist&amp;quot;
    curl -s -X POST &amp;quot;https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules&amp;quot; \
    -H &amp;quot;Content-Type:application/json&amp;quot; \
    -H &amp;quot;X-Auth-Key:${KEY}&amp;quot; \
    -H &amp;quot;X-Auth-Email:${EMAIL}&amp;quot; \
    --data &#39;{&amp;quot;mode&amp;quot;:&amp;quot;block&amp;quot;,&amp;quot;configuration&amp;quot;:{&amp;quot;target&amp;quot;:&amp;quot;ip&amp;quot;,&amp;quot;value&amp;quot;:&amp;quot;&#39;&amp;quot;${ip}&amp;quot;&#39;&amp;quot;},&amp;quot;notes&amp;quot;:&amp;quot;This site is not available through tor endpoints&amp;quot;}&#39; &amp;gt; /dev/null
# else
#   echo &amp;quot;already in blocklist&amp;quot;
  fi
done

&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Block almost all tor traffic to your server</title>
      <link>https://aapjeisbaas.nl/post/block-almost-all-tor-traffic-to-your-server/</link>
      <pubDate>Wed, 09 Mar 2016 01:15:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/block-almost-all-tor-traffic-to-your-server/</guid>
      <description>&lt;p&gt;![tor logo] ({filename}/static/images/tor.png)&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t want to allow access to your server through the tor network you can ask nicely or just add every malicious looking client to a list.
I tried to come up with a better solution.&lt;/p&gt;
&lt;p&gt;This Script takes the known tor endpoints from torproject.org and adds it to a ipset list.
The ipset is the dropped with iptables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash

echo &amp;quot;Tor endpoint list loading&amp;quot;
TORLIST=$(curl -s https://check.torproject.org/exit-addresses |grep ExitAddress | awk &#39;{print $2 }&#39; | sort | uniq)

echo &amp;quot;creating ipset tor list&amp;quot;
ipset destroy torset
ipset -N torset iphash

for ip in ${TORLIST}; do
    ipset -A torset ${ip}
done
iptables -A INPUT -m set --match-set torset src -j DROP
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
    <item>
      <title>Copy mysql database to dev location</title>
      <link>https://aapjeisbaas.nl/post/copy-mysql-database-to-dev-location/</link>
      <pubDate>Mon, 01 Feb 2016 11:10:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/copy-mysql-database-to-dev-location/</guid>
      <description>&lt;p&gt;![db copy] ({filename}/static/images/db_copy.jpg)&lt;/p&gt;
&lt;p&gt;If you are just here for the script copy the script and have fun.
If you want a bit more info read on this script is tweakable to some extend.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#! /bin/bash
 
TIMESTAMP=$(date +&amp;quot;%F&amp;quot;)
BACKUP_DIR=&amp;quot;/tmp/db-dump/$TIMESTAMP&amp;quot;
MYSQL_USER=&amp;quot;root&amp;quot;
MYSQL=/usr/bin/mysql
MYSQL_PASSWORD=&amp;quot;password&amp;quot;
MYSQLDUMP=/usr/bin/mysqldump

mkdir -p &amp;quot;$BACKUP_DIR&amp;quot;

databases=&amp;quot;live_db&amp;quot;
for db in $databases; do
  test_db=$db&amp;quot;_test&amp;quot;
  $MYSQLDUMP --user=$MYSQL_USER -p$MYSQL_PASSWORD $db | gzip &amp;gt; &amp;quot;$BACKUP_DIR/$db.sql.gz&amp;quot;
  mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;DROP DATABASE IF EXISTS $test_db;&amp;quot;
  mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;CREATE DATABASE $test_db;&amp;quot;
  zcat &amp;quot;$BACKUP_DIR/$db.sql.gz&amp;quot; | mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD $test_db
done

rm -rf &amp;quot;$BACKUP_DIR&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This script works single threaded and can take in multiple databases or even all databaeses and copy them to _test databases.
If you want it to copy all databases to _test use this line for defining the databases var:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;databases=$($MYSQL --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;SHOW DATABASES;&amp;quot; | grep -Ev &amp;quot;(Database|information_schema|performance_schema)&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The biggest issue with this method is that it is slow and you may want to consider skipping disk and pass output of the dump straight into the import with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;  test_db=$db&amp;quot;_test&amp;quot;
  mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;DROP DATABASE IF EXISTS $test_db;&amp;quot;
  mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD -e &amp;quot;CREATE DATABASE $test_db;&amp;quot;
  $MYSQLDUMP --user=$MYSQL_USER -p$MYSQL_PASSWORD $db | mysql --user=$MYSQL_USER -p$MYSQL_PASSWORD $test_db
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is a downside to this method, while you are copying the database your data in the destination location is longer unavailable.
The database is dropped and then filled as it comes in from the dump instead of being parsed at max read speed of disk or max execute speed of mysql.&lt;/p&gt;
&lt;p&gt;The last method is realy good for a seperate dev mysql server but can fill up your ram if the dev mysql server can not keep up with the dump speed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Let&#39;s Encrypt</title>
      <link>https://aapjeisbaas.nl/post/lets-encrypt/</link>
      <pubDate>Tue, 15 Dec 2015 22:17:00 +0100</pubDate>
      <guid>https://aapjeisbaas.nl/post/lets-encrypt/</guid>
      <description>&lt;p&gt;![Let&amp;rsquo;s encrypt] ({filename}/static/images/letsencrypt.svg)&lt;/p&gt;
&lt;p&gt;This Article is all about the SSL cert signing and auto signing for Let&amp;rsquo;s Encrypt.
I only cover nginx based setup and does require root and knowledge of some basic nginx vhost configuration.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with what Let&amp;rsquo;s Encrypt is and how to use it.
It is a public CA that signs for free, there are some limits but reaching them is not likeley in most cases.
To Get a valid cert they use their own  application that is developed in tho open on this 
&lt;a href=&#34;https://github.com/letsencrypt/letsencrypt&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;github page.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Installing the client is pretty simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$ git clone https://github.com/letsencrypt/letsencrypt
user@server:~$ cd letsencrypt
user@server:~/letsencrypt$ ./letsencrypt-auto --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have the client you can request a first cert:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;./letsencrypt-auto certonly -a webroot --webroot-path=/var/www/your/web/root -d test.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this to work you need the have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the domain &lt;code&gt;test.yourdomain.com&lt;/code&gt; going to the dir &lt;code&gt;/var/www/your/web/root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;write permisions in the folder&lt;/li&gt;
&lt;li&gt;allow for the following location to be reached from a external server &lt;code&gt;http://test.yourdomain.com/.well-known/acme-challenge/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last point is vital for checking if you are the owner of the domain and if you are like me and 403 everything starting with &amp;ldquo;.&amp;rdquo; then this will fail and you need to work around it in your vhost conf.&lt;/p&gt;
&lt;p&gt;I tackle this all in one simple nginx include:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$ cat /etc/nginx/includes/ssl-validate.conf
location ~ acme-challenge {
    root        /etc/letsencrypt/temp;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets the root dir for anything that looks like a acme-challenge to: &lt;code&gt;/etc/letsencrypt/temp&lt;/code&gt;
This needs to be included in the top of every port 80 vhost you want to have this work for.&lt;code&gt;include /etc/nginx/includes/ssl-validate.conf;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That was half of the equation the &amp;ldquo;webroot&amp;rdquo; part of it now we need to get some domains in a list, a script to request cert and a cronjob to do this automnomous.&lt;/p&gt;
&lt;p&gt;First you need to put the repo checkout in a sensible place:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$ cd /usr/local/bin/
user@server:~$ git clone https://github.com/letsencrypt/letsencrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need a script to check and manage the certs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$  cat /usr/local/bin/ssl-renew
#vhosts=$(ls -lah /var/log/nginx/*access*log |awk &#39;{print $9}&#39; | sed &#39;s/\/var\/log\/nginx\///g&#39; | sed &#39;s/.access.log//g&#39; |grep -ve &#39;*\|access.log&#39; |uniq |sort -h)

vhosts=&amp;quot;
test.yourdomain.com
test.your-other-domain.com
&amp;quot;

export DIR=/etc/letsencrypt/temp
mkdir -p $DIR

clear
for i in $vhosts
do
  echo &amp;quot;-------------------------------------------------------------------------------------------&amp;quot;
  echo $i

  if openssl x509 -checkend 604800 -noout -in /etc/letsencrypt/live/$i/cert.pem
  then
    echo &amp;quot;Certificate is good for at least another week&amp;quot;
  else
    echo &amp;quot;Certificate has expired or will do so within 7 days!&amp;quot;
    echo &amp;quot;(or is invalid/not found)&amp;quot;
    echo &amp;quot;starting renew procedure&amp;quot;
    /usr/local/bin/letsencrypt/letsencrypt-auto --renew certonly -a webroot --webroot-path=$DIR -d $i
    /usr/sbin/nginx -t &amp;amp;&amp;amp; /usr/sbin/nginx -s reload
  fi

done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see there are 2 ways of loading the vhosts in to the loop from a simpje list of from the names in the nginx access logs my filenames look like: &lt;code&gt;some-subdomain.aapjeisbaas.nl.access.log&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For now just stick with the list if you need more flexibility you cat try the nginx access log trick.&lt;/p&gt;
&lt;p&gt;This script checks the cert is in the default location and if it is valid for at least a week.
If one of those things is not true it will request if it is not found or renew a cert. (for extra security remove &lt;code&gt;--renew&lt;/code&gt; to generate a new key for every cert.)&lt;/p&gt;
&lt;p&gt;Now chmod the file, run it and check output debug where needed. If all goes well add it to a cron file:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$  cat /etc/cron.d/ssl-check 
7 2 * * * root  /usr/local/bin/ssl-renew | /sbin/sendmail -F mail@domain.com -t mail@domain.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The certs are in &lt;code&gt;/etc/letsencrypt/live/&lt;/code&gt; every domain has their own folder. Here is a example nginx conf for the test domain.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;user@server:~$ cat /etc/nginx/conf.d/test.domain.com.conf
server {
    listen      80;
    listen      [::]:80;
    server_name test.domain.com;
    include     /etc/nginx/includes/ssl-validate.conf;
    location / {
        return      302 https://test.domain.com/;
    }
server {
    listen      443;
    listen      [::]:443;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/test.domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/test.domain.com/privkey.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:DHE-RSA-AES128-SHA:DHE-RS-AES256-SHA:EDH-RSA-DES-CBC3-SHA:ECDHE:DHE:HIGH:!MD5:!aNULL:!EDH:!RC4;
    ssl_prefer_server_ciphers on;
    ssl_stapling on;
    resolver your.closest.dns.resolver; # maybe use 8.8.8.8 if you want


    server_name test.domain.com;

    include     /etc/nginx/includes/log.conf;

    root        /var/www/vhosts/test.domain.com;

    location / {
      add_header &amp;quot;Cache-Control&amp;quot; $cacheable_types;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
    </item>
    
  </channel>
</rss>
