<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
        <title>A Student Hacker&apos;s Life</title>
        <description>A Student Hacker&apos;s Life - Mickaël</description>
        <link>https://blog.securem.eu</link>
        <atom:link href="https://blog.securem.eu/rss" rel="self" type="application/rss+xml" />
        <lastBuildDate>Sun, 03 May 2026 15:19:10 +0000</lastBuildDate>
        <pubDate>Sun, 03 May 2026 15:19:10 +0000</pubDate>
        <ttl>60</ttl>


        <item>
                <title>Protip: don&apos;t use &apos;yarn install&apos; on your CI pipeline</title>
                <description>
&lt;p&gt;I recently discovered the &lt;a href=&quot;https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-frozen-lockfile&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--frozen-lockfile&lt;/code&gt;
parameter&lt;/a&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn
install&lt;/code&gt;, while randomly reading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn&lt;/code&gt;’s documentation &lt;small&gt;(yes I’m weird)&lt;/small&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Don’t generate a yarn.lock lockfile and fail if an update is needed.&lt;/span&gt;
yarn &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--frozen-lockfile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is super useful on a CI pipeline to &lt;strong&gt;make sure we developers don’t forget to update the lockfile
when we add a dependency&lt;/strong&gt;. In theory, it should never happen but sometimes people forget to commit
some parts of the changes - so using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--frozen-lockfile&lt;/code&gt; will save you a new commit or a new PR
when it happens.&lt;/p&gt;

&lt;p&gt;So whatever CI pipeline you are using, from CircleCI to a self-hosted Jenkins server, that’s one
more way of catching quality issues before they get merged ;)&lt;/p&gt;

&lt;h3 id=&quot;its-also-available-in-other-languages&quot;&gt;It’s also available in other languages:&lt;/h3&gt;

&lt;p&gt;If you are also using Ruby for some of your services like we do at &lt;a href=&quot;https://angel.co/company/akidolabs&quot;&gt;Akido
Labs&lt;/a&gt;, bundler has a similar option:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bundle &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--frozen&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python’s pipenv doesn’t look like it has a similar option yet, though. Please let me know if other
languages are offering the same feature, I’ll be happy to add them to this post.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/protip/2019/06/14/protip-dont-use-yarn-install-on-your-ci-pipeline/</link>
                <guid>https://blog.securem.eu/protip/2019/06/14/protip-dont-use-yarn-install-v1160info-no-lockfile-found14-resolving-packages24-fetching-packages34-linking-dependencies44-building-fresh-packagessuccess-saved-lockfiledone-in-016s-on-your-ci-pipeline</guid>
                <pubDate>Fri, 14 Jun 2019 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Why you should not use test coverage as a KPI</title>
                <description>
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: &lt;em&gt;Do not use test coverage as a proxy for your application quality. While a low coverage is a sign of a badly tested product, a high coverage has no real meaning in terms of quality.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently, I was surprised to see “test coverage” being added to the list of &lt;a href=&quot;https://en.wikipedia.org/wiki/OKR&quot;&gt;OKRs&lt;/a&gt; of my engineering team. This meant test coverage would become a metric we would track and that would help us ship higher-quality applications.&lt;/p&gt;

&lt;h2 id=&quot;why-it-looks-like-a-good-idea&quot;&gt;Why it looks like a good idea&lt;/h2&gt;

&lt;p&gt;First of all, let’s define what it is: &lt;strong&gt;test coverage is the percentage of unique logical lines of code in your codebase that is executed during the tests, out of the total number of lines of the codebase&lt;/strong&gt;.
A test coverage of 95% then means that your tests executed almost all your codebase. How cool is that?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/code-coverage.png&quot; alt=&quot;Example code coverage report for HowFast Monitoring&quot; /&gt;&lt;/p&gt;
&lt;div style=&quot;text-align: center; font-style: italic;&quot;&gt;Example code coverage on the source code of &lt;a href=&quot;https://www.howfast.tech/&quot;&gt;HowFast Monitoring&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;Tests are used to make sure the code performs its intended duty, in a repeatable and automated way. More tests usually means that a bug introduced can caught be more easily, leading to improved application quality.&lt;/p&gt;

&lt;p&gt;Finally, picking the right metrics for product quality is &lt;strong&gt;hard&lt;/strong&gt;. It depends heavily on what your product does (response time? clarity of the language so that  anyone can easily understand what’s going on? time or number of clicks required to book your train ticket?). Code coverage is a metric that applies to all applications as long as they are made of code, so it’s tempting to pick it up.&lt;/p&gt;

&lt;p&gt;However, code coverage is not a good proxy for your product’s quality.&lt;/p&gt;

&lt;h2 id=&quot;why-and-how-it-could-go-wrong&quot;&gt;Why and how it could go wrong&lt;/h2&gt;

&lt;p&gt;When building &lt;a href=&quot;https://www.linkedin.com/company/géoschool/&quot;&gt;GéoSchool&lt;/a&gt; back in the days, I wanted to reach 100% test coverage on all the Python code I was writing. Let’s consider the following test I once wrote:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_bullshit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; This test should really be improved &quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/api/markers_meta.json&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Let&apos;s call it a day!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It basically calls a route, and executes the underlying controller. Then it checks whether the HTTP status code of the response is 200 - meaning there was no server error. The good side is that it will detect if the server explodes on this request. The other good side is that it allowed me to reach my target of 100% code coverage. Awesome, now my app will have no bug with such a high test coverage! Of course I was dumb.&lt;/p&gt;

&lt;p&gt;This test is not completely assertion-free, but we don’t check anything about the response. The controller could just return an empty JSON response and the test would still pass. This high-coverage test doesn’t actually do any business logic verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the perfect example of how test coverage can go wrong and not be a proxy for product quality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Regarding GéoSchool, I then lowered the standard from 100% to 80% and told our CI platform to reject any build below this threshold, so that developers would have to write tests on the feature they were adding.
Yes, I still hadn’t learned the lesson.
This led to other issues, like developers writing &lt;strong&gt;assertion-free tests&lt;/strong&gt; to be able to merge their code. One interesting issue  was that PRs removing code would fail the build because they decreased the overall ratio (if you burn code that is covered by tests, and keep code that is not tested, the coverage falls).&lt;/p&gt;

&lt;p&gt;I finally learned from this mistake and I don’t pay much attention to code coverage nowadays.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;Also on a more philosophical note, I know we are supposed to get wiser as we age, but it seems to just be that we are more able to see why we were being really stupid only a few years back. I’m afraid of what I’m going to discover in the next year about all the stupid things I did this year. Still, I guess some people do call this wisdom.&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-to-do-instead&quot;&gt;What to do instead&lt;/h2&gt;

&lt;p&gt;One thing is clear: low coverage is a good indicator of low-quality testing. It does mean your tests don’t even execute - let alone test - a good chunk of your code. &lt;strong&gt;Having a minimum coverage to respect&lt;/strong&gt; can be a goal, even though it won’t guarantee that these (few) tests will catch any bug. But while a high test coverage doesn’t mean the application is well tested, a low coverage is definitely a sign that it is &lt;strong&gt;not&lt;/strong&gt; tested.&lt;/p&gt;

&lt;p&gt;By running a company and working with other talented CTOs I also learned other interesting things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Writing tests and maintaining them is hard and takes time (usually from 30% to 100% of the time required to code the feature).&lt;/li&gt;
  &lt;li&gt;You need to balance the quality of your product with the time you are ready to invest. If you can achieve 80% of the quality in only 20% of the time then do it, and spend the time on something more important. This is especially true in startups where the product will change quite often as you iterate on what your users / the market need. Just stick a big “beta” label on your product until you know the market wants it and then proceed to improve the quality and reliability.&lt;/li&gt;
  &lt;li&gt;As a result, &lt;strong&gt;I don’t recommend investing time in writing tests if you haven’t reached the &lt;a href=&quot;https://en.wikipedia.org/wiki/Product/market_fit&quot;&gt;Product-Market Fit&lt;/a&gt;&lt;/strong&gt;, because you may need to throw away half of your product if the market doesn’t need it.&lt;/li&gt;
  &lt;li&gt;You can choose to write no tests and to instead manually test the critical path of your app before each deploy. Important bugs will be caught by this manual testing and small bugs would be caught by &lt;a href=&quot;https://medium.com/@msuixo/configure-sentry-for-vue-js-project-6aa07efe2c80&quot;&gt;Sentry&lt;/a&gt; when your users hit them. Not ideal but if you have a lot of users, it could make sense to save time on the test maintenance and use it on manual testing. I call this the “Sentry-driven deployment”, but I don’t recommend it unless you really know what you’re doing ;)&lt;/li&gt;
  &lt;li&gt;One last note about testing and code coverage: it’s fine to not cover branches that should almost never be executed, unless there is a high importance accorded to this branch (“you won the lottery!”). You’re going to invest time/resource into testing something that is not important. The same is also true about language features: you want to focus on testing the business logic of your code, but not the language feature - after all it’s not your job to make sure that the language you’re using does its job.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just to be clear, I do &lt;strong&gt;not&lt;/strong&gt; advise against writing tests: sometimes they will actually help you ship the feature faster, for instance when it’s easier to test your code using a test than to test it manually by pushing buttons in your application.
&lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;Test-Drive Development (TDD)&lt;/a&gt; is then a very powerful way to go, and will guarantee that your tests actually test the business logic.
And the time you save now by not writing tests now is converted into tech debt, that will sooner or later bite you and slow you down. It’s all about balancing the two, and “when to write tests” is a whole piece and will maybe become another blog post.&lt;/p&gt;

&lt;p&gt;Overall, if your goal is to measure the quality of your product, &lt;strong&gt;do not use code coverage as a metric&lt;/strong&gt;. Instead, you can use the following checklist items, at the end of every sprint:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;did we discover any regression (a previously working feature stopped working unexpectedly) after merging the code?&lt;/li&gt;
  &lt;li&gt;are the developers proud of the source code?&lt;/li&gt;
  &lt;li&gt;do we meet our standards (goals) in term of performance (you can use &lt;a href=&quot;https://www.howfast.tech/&quot;&gt;HowFast&lt;/a&gt; to measure this), uptime, SEO, - insert whatever your product quality means here -, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In terms of quantitative metric, we still have the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;number of issues reported by your users or by error reporting tools like &lt;a href=&quot;https://sentry.io/&quot;&gt;Sentry&lt;/a&gt; (which can also report the number of regressions). The lower the better.&lt;/li&gt;
  &lt;li&gt;how much does the customer(s)/user(s) like the application? Rate 1-10.&lt;/li&gt;
  &lt;li&gt;any customer-focus result metric: time saved for the user, number of projects created inside your app, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this has been useful. Please let me know if you disagree or if you have comments about this, I’d be happy to jump in a chat &lt;a href=&quot;https://twitter.com/msuixo/&quot;&gt;on Twitter&lt;/a&gt; or by email (see the footer of this page). You can also comment on the &lt;a href=&quot;https://news.ycombinator.com/item?id=19319780&quot;&gt;Hacker News thread&lt;/a&gt;.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/opinions/2019/03/05/why-you-should-not-use-test-coverage-as-a-kpi/</link>
                <guid>https://blog.securem.eu/opinions/2019/03/05/why-you-should-not-use-test-coverage-as-a-kpi</guid>
                <pubDate>Tue, 05 Mar 2019 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Linux users: running docker without sudo is dangerous</title>
                <description>
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: unless you are using a docker-machine (default on OSX and Windows), &lt;strong&gt;running docker commands equals being root on the host&lt;/strong&gt;. If you added yourself in the docker group or changed the permission of the Docker socket, you (or a malicious program) &lt;strong&gt;can become root without needing the password&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a technical mentor for startups, I often get to talk with CTOs. A few days ago, I discovered that one of them was using docker on his Linux laptop without having to use sudo. This is very dangerous and you should know the consequences.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/docker-logo-horizontal.png&quot; alt=&quot;docker logo&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;docker-engine&quot;&gt;Docker Engine&lt;/h2&gt;

&lt;p&gt;Docker is awesome. Replicating a MongoDB database locally, spinning up a Redis server or a Selenium node in seconds, versioning your app’s requirements, all this substantially speeds up our developer lives.&lt;/p&gt;

&lt;p&gt;As we type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo docker run&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo docker-compose logs&lt;/code&gt;, etc. several times per day or per hour, we might be tempted to drop the “sudo” part to avoid typing a password every 20 minutes. The two major “classic” solutions are either:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;adding yourself in a “docker” group&lt;/li&gt;
  &lt;li&gt;changing the permission on the docker socket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this is your case or if you plan to use one of these technique (or any other, actually), you need to know something:&lt;/p&gt;

&lt;p&gt;&lt;big&gt;Accessing the Docker Engine equals having root access on the host&lt;/big&gt;&lt;/p&gt;

&lt;p&gt;Why so? Let’s take a practical example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker run -v /:/hostOS -i -t ubuntu

root@7a064e899aa3:/# whoami
root
root@7a064e899aa3:/# cat /hostOS/etc/shadow
at:!:17339::::::
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yeah, I just read the content of a file only root can read, and which contains my (encrypted) password a few lines after. Obviously, I can also write to it to change the password, or create a new user with root privileges, etc.&lt;/p&gt;

&lt;h2 id=&quot;what-happened&quot;&gt;What happened&lt;/h2&gt;

&lt;p&gt;Thanks to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; option, we “mounted” the host root folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/hostOS/&lt;/code&gt; folder inside the container.&lt;/p&gt;

&lt;p&gt;Running any container image with the root user (default for the majority of images) gives us root privileges inside the container, thus… root privileges over all the files on the host. Now, you should start to understand that it allows you to read (and write!) any file, including the list of system passwords (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/shadow&lt;/code&gt;), any file containing credentials, and finally executing any command.&lt;/p&gt;

&lt;p&gt;One could even create a root-owned program with the setuid bit set, allowing anybody executing it to execute it as root. Such a program could be used without Docker, by virtually anybody on the system, and even after the docker permission issue is fixed.&lt;/p&gt;

&lt;p&gt;On OSX and Windows, the Docker Engine cannot typically directly execute on the host, so you have to setup a Docker Machine, which is basically a Virtual Machine running a derivative of Linux, and in which the Docker Engine is executed. On these systems, you gain root privileges on the VM itself and not directly on the host. There are still shared volumes with the actual host, and VM evasion vulnerabilities, though.&lt;/p&gt;

&lt;h2 id=&quot;understanding-the-risk&quot;&gt;Understanding the risk&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Ok, but I am the only user on my machine, so why bother?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a large number of reasons why you don’t run everything as root on your machine, and two are particularly important:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;users execute untrusted code (ever copy-pasted a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl https://.../script.sh | sh&lt;/code&gt; command?)&lt;/li&gt;
  &lt;li&gt;software you execute have vulnerabilities (something as widespread as the Atom editor recently let almost everybody execute anything on your computer: see https://statuscode.ch/2017/11/from-markdown-to-rce-in-atom/) - this is more frequent than you think!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On &lt;strong&gt;servers&lt;/strong&gt;, there are typically multiple users (or there should be): one per developer or team, one for the continuous deployment, etc. This is even more dangerous, since servers usually take care of sensitive data (passwords, personal information, more credentials, etc).&lt;/p&gt;

&lt;h2 id=&quot;what-to-donow&quot;&gt;What to do now?&lt;/h2&gt;

&lt;p&gt;Now that you know what removing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; prefix actually does, you have two solutions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;always use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;: this ensures you are allowed to become root otherwise, and reminds you of the root powers you are gaining&lt;/li&gt;
  &lt;li&gt;accept the risk associated to opening a &lt;strong&gt;privilege escalation vulnerability&lt;/strong&gt; on your system, and keep in mind that anybody or any (flawed or malicious) program using the docker command will gain root-like privileges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I obviously recommend the first solution ;)&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Found this article useful? Share it with your colleagues or friends!&lt;/p&gt;

&lt;p&gt;Have a question? Ask it here or tweet me at &lt;a href=&quot;https://twitter.com/msuixo&quot;&gt;@msuixo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;See also &lt;a href=&quot;https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface&quot;&gt;https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface&lt;/a&gt;&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2018/07/09/linux-users-running-docker-without-sudo-is-dangerous/</link>
                <guid>https://blog.securem.eu/serverside/2018/07/09/linux-users-running-docker-without-sudo-is-dangerous</guid>
                <pubDate>Mon, 09 Jul 2018 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>GitHub Pages generated a (rogue?) TLS cert for my own domain!</title>
                <description>
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: This blog is hosted on GitHub Pages, with a custom domain and over HTTPS. GitHub/Fastly
generated the certificate without me knowing, and while this is not supposed to happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT: this is a (silent) gradual release of HTTPS support for custom-domain websites hosted on GitHub
Pages. Read more at &lt;a href=&quot;#final-note&quot;&gt;the end of this post&lt;/a&gt;. While it shows the certificate is not
“rogue”, having certs generated without you knowing or expecting it (this is not an official
feature) remains surprising and unpleasant.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;this-blog-is-hosted-on-github-pages&quot;&gt;This blog is hosted on GitHub Pages&lt;/h2&gt;

&lt;p&gt;This blog is hosted on GitHub Pages, through Fastly (the CDN used by GitHub).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ host blog.securem.eu
blog.securem.eu is an alias for mickaelbergem.github.io.
mickaelbergem.github.io is an alias for sni.github.map.fastly.net.
sni.github.map.fastly.net has address 151.101.17.147
sni.github.map.fastly.net has IPv6 address 2a04:4e42:1d::403
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I do have a custom domain (“blog.securem.eu”), ie not a *.github.io one.&lt;/p&gt;

&lt;p&gt;I am &lt;strong&gt;not using CloudFlare&lt;/strong&gt;, which is commonly used to add HTTPS on top of a GitHub Pages website.&lt;/p&gt;

&lt;h2 id=&quot;github-pages-does-not-allow-you-to-use-https-on-custom-domains&quot;&gt;GitHub Pages does not allow you to use HTTPS on custom domains&lt;/h2&gt;

&lt;p&gt;This is understandable as it would mean generating, storing and deploying a large number of
certificates for domains not owned by GitHub. I created a test (GitHub Pages website) at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test-github.securem.eu&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/github-custom-domain-https.png&quot; alt=&quot;GitHub does not allow HTTPS for custom domains&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the official documentation, it is clearly stated:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can enforce HTTPS to add a layer of encryption for traffic to your GitHub Pages site if it has a github.io domain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Trying to access this website through HTTPS will raise an security error:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The certificate presented is only valid for www.github.com, *.github.com, github.com, *.github.io, github.io, *.githubusercontent.com, githubusercontent.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As of today, people have used CloudFlare to bring HTTPS to their custom-domain GitHub-Pages-hosted websites, which is not the case here.&lt;/p&gt;

&lt;h2 id=&quot;wtf&quot;&gt;WTF&lt;/h2&gt;

&lt;p&gt;We have:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;This blog is hosted on GitHub Pages with a custom domain, and no CloudFlare proxy.&lt;/li&gt;
  &lt;li&gt;GitHub Pages does not serve content over HTTPS for custom-domain websites&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, we should get an error when trying to access it over HTTPS:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ curl https://blog.securem.eu -v
*   Trying 151.101.17.147...
* TCP_NODELAY set
* Connected to blog.securem.eu (151.101.17.147) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=blog.securem.eu
*  start date: Apr 15 15:48:38 2018 GMT
*  expire date: Jul 14 15:48:38 2018 GMT
*  subjectAltName: host &quot;blog.securem.eu&quot; matched cert&apos;s &quot;blog.securem.eu&quot;
*  issuer: C=US; O=Let&apos;s Encrypt; CN=Let&apos;s Encrypt Authority X3
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x56280a3da620)
&amp;gt; GET / HTTP/2
&amp;gt; Host: blog.securem.eu
&amp;gt; User-Agent: curl/7.59.0
&amp;gt; Accept: */*
&amp;gt;
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
&amp;lt; HTTP/2 200
&amp;lt; server: GitHub.com
&amp;lt; content-type: text/html; charset=utf-8
&amp;lt; last-modified: Tue, 17 Apr 2018 14:40:47 GMT
&amp;lt; access-control-allow-origin: *
&amp;lt; expires: Wed, 18 Apr 2018 10:24:28 GMT
&amp;lt; cache-control: max-age=600
&amp;lt; x-github-request-id: D89E:34CA:4DBEB7:6CC5E4:5AD71A84
&amp;lt; accept-ranges: bytes
&amp;lt; date: Wed, 18 Apr 2018 10:14:29 GMT
&amp;lt; via: 1.1 varnish
&amp;lt; age: 1
&amp;lt; x-served-by: cache-lcy19247-LCY
&amp;lt; x-cache: HIT
&amp;lt; x-cache-hits: 1
&amp;lt; x-timer: S1524046470.828003,VS0,VE0
&amp;lt; vary: Accept-Encoding
&amp;lt; x-fastly-request-id: 2e84e2cab76ec88bccbc696a0c4984618c7f2a5f
&amp;lt; content-length: 9260
&amp;lt;


&amp;lt;!DOCTYPE html&amp;gt;
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oopsie?&lt;/p&gt;

&lt;p&gt;So, yes, &lt;strong&gt;this very website is served by GitHub Pages over HTTPS&lt;/strong&gt;. With a Let’s Encrypt certificate generated a few days ago. Here is the certificate in the Certificate Transparency logs: &lt;a href=&quot;https://crt.sh/?id=399789700&quot;&gt;https://crt.sh/?id=399789700&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I did not generate this certificate&lt;/strong&gt;. GitHub (or Fastly, or someone else) did. And GitHub has the private key associated with the certificate, since they are serving the content over a trusted HTTPS connection.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/github-https-blog.png&quot; alt=&quot;GitHub sometimes allow HTTPS for custom domains&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion?&lt;/h2&gt;

&lt;p&gt;I still do not explain how this happened. The best guess is that GitHub is preparing for automatic Let’s Encrypt certificate generation for custom domains, and tested it on a small (?) subset of the repositories.&lt;/p&gt;

&lt;p&gt;One way to confirm this would be to check in the Certificate Transparency logs what other certificates have been generated around the same date and time, to see if other custom-domain GitHub Pages websites got new certificates. I didn’t have time yet to investigate further.&lt;/p&gt;

&lt;p&gt;Anyway, this looks weird since GitHub generated a certificate for a domain I own without notifying me or asking whether I was OK with it. I could have used &lt;a href=&quot;https://blog.securem.eu/serverside/2017/07/02/dns-caa-what-why-and-how-this-new-pki-control-mechanism/&quot;&gt;DNS CAA&lt;/a&gt;, or alerts set on the Certificate Transparency logs to prevent this or be notified of this “rogue” certificate.&lt;/p&gt;

&lt;p&gt;Let me know if you think of any other possible explanation, or if you know something about it.&lt;/p&gt;

&lt;p&gt;What is most disturbing is the fact that HTTPS seems to work only on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog.securem.eu&lt;/code&gt;. Renaming to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog-test.securem.eu&lt;/code&gt; shows the usual behaviour (with no HTTPS), but switching back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blog.securem.eu&lt;/code&gt; enables HTTPS again.&lt;/p&gt;

&lt;p&gt;PS: I am not mad at GitHub for doing it, just surprised since I wasn’t expecting it and wasn’t notified about it. In the end I am even happy to benefit from HTTPS, even if I feel less in control of what happens. The GitHub team does a really great job, and I am very happy to be able to use GitHub Pages for free (even without HTTPS).&lt;/p&gt;

&lt;hr id=&quot;final-note&quot; /&gt;

&lt;p&gt;&lt;big&gt;
Thanks on comments on &lt;a href=&quot;https://news.ycombinator.com/item?id=16866208#16866487&quot;&gt;the HackerNews thread&lt;/a&gt;, I discovered &lt;a href=&quot;https://github.com/isaacs/github/issues/156#issuecomment-374870960&quot;&gt;this thread&lt;/a&gt; that suggests that &lt;strong&gt;the
feature is progressively rolled out by GitHub&lt;/strong&gt;. There is also an “official confirmation” &lt;a href=&quot;https://gist.github.com/coolaj86/e07d42f5961c68fc1fc8#gistcomment-2370070&quot;&gt;in this gist&lt;/a&gt;.
&lt;/big&gt;&lt;/p&gt;

&lt;p&gt;&lt;big&gt;
Hence, there is no evil attack taking place, but instead GitHub Pages silently doing DV (Domain
Validation) to generate certificates, since a few weeks.
&lt;/big&gt;&lt;/p&gt;

&lt;p&gt;What started as an unpleasant surprise (WTF is this certificate doing here?) changed to a happy
ending, since I got HTTPS deployed on this blog (by the GitHub team).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I still encourage you to set up Certificate Transparency alerts to be sure all certificates
generated for your domains are legit.&lt;/strong&gt; I am currently testing &lt;a href=&quot;https://sslmate.com/certspotter/&quot;&gt;CertSpotter by SSLMate&lt;/a&gt;
(free up to 5 domains, covers all the subdomains).&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2018/04/18/github-pages-generated-a-tls-cert-for-my-own-domain/</link>
                <guid>https://blog.securem.eu/serverside/2018/04/18/github-pages-generated-a-tls-cert-for-my-own-domain</guid>
                <pubDate>Wed, 18 Apr 2018 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Versioned DNS records with Gandi&apos;s LiveDNS</title>
                <description>
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I wrote a tool to manage DNS records through any GIT repository, hence versioning the zone
records. It uses Gandi’s LiveDNS API.&lt;/p&gt;

&lt;p&gt;When updating my DNS records, I need to use the web interface of my registrar (Gandi). The zone records are
versioned, but I can’t easily display diffs, let users propose changes through Pull Requests, or
comment on potential implications of changes before applying the change. The best source version control
available today is &lt;strong&gt;git&lt;/strong&gt;, so why not using it for DNS zones?&lt;/p&gt;

&lt;p&gt;Being a supporter of &lt;strong&gt;Infrastructure-as-Code&lt;/strong&gt;, I wrote a small
Python tool to &lt;em&gt;manage my DNS records from a versioned (GIT) repository&lt;/em&gt;. You need to be a user of Gandi’s LiveDNS, 
or you will have to adapt the tool to your registrar’s API.&lt;/p&gt;

&lt;h2 id=&quot;configuration&quot;&gt;Configuration&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Fork the main repository: &lt;a href=&quot;https://github.com/MickaelBergem/gandi-livedns-zone-manager&quot;&gt;https://github.com/MickaelBergem/gandi-livedns-zone-manager&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Clone it locally&lt;/li&gt;
  &lt;li&gt;Retrieve your LiveDNS API key: go to &lt;a href=&quot;https://account.gandi.net/&quot;&gt;your Gandi account&lt;/a&gt;, under “Security”, “Generate your API key”.&lt;/li&gt;
  &lt;li&gt;Put your key in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api_key.txt&lt;/code&gt; file in the repository&lt;/li&gt;
  &lt;li&gt;Test your configuration by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./livedns.py view&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This should output your current zones:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./livedns.py view
&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; Zone test-zone &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;580549ec-c25f-11e7-9d8f-00163e6dc886] &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;
        No domain associated with this zone.
        1 records &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;this zone:

        CNAME   10800   &lt;span class=&quot;nb&quot;&gt;test                &lt;/span&gt;access.mail.gandi.net.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Pull the zones and write them on disk&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./livedns.py pull
&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; Zone test-zone &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;580549ec-c25f-11e7-9d8f-00163e6dc886] &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;
        Writing... &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;test-zone_580549ec-c25f-11e7-9d8f-00163e6dc886.txt&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; zones
&lt;span class=&quot;nt&quot;&gt;-rw-r--r--&lt;/span&gt; 1 suixo &lt;span class=&quot;nb&quot;&gt;users  &lt;/span&gt;133 24 nov.  00:06 test-zone_580549ec-c25f-11e7-9d8f-00163e6dc886.txt
&lt;span class=&quot;c&quot;&gt;# Now make some changes in your zone files&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;vim zones/test-zone_580549ec-c25f-11e7-9d8f-00163e6dc886.txt
&lt;span class=&quot;c&quot;&gt;# And push the changes to your registrar &lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./livedns.py push
Found zone test-zone &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;580549ec-c25f-11e7-9d8f-00163e6dc886&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
        Uploading the zone... ok
        Server answered: DNS Zone Record Created
&lt;span class=&quot;c&quot;&gt;# The changes are immediately effective&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;dig CNAME test.test-domain.org
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; ANSWER SECTION:
test.test-domain.org. 10800    IN      CNAME   access.mail.gandi.net.
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;benefits&quot;&gt;Benefits&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;you can manage the zone files exactly like usual source files, with your favorite Version Control System such as git&lt;/li&gt;
  &lt;li&gt;if you are using GitHub or a similar platform, your collaborators can submit Pull Requests to change the zone file&lt;/li&gt;
  &lt;li&gt;changes are effective immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this little tool will help you, do not hesitate to share suggestions or to report bugs on &lt;a href=&quot;https://github.com/MickaelBergem/gandi-livedns-zone-manager&quot;&gt;the project page&lt;/a&gt; :)&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2017/11/24/versioned-dns-records-with-gandis-livedns/</link>
                <guid>https://blog.securem.eu/serverside/2017/11/24/versioned-dns-records-with-gandis-livedns</guid>
                <pubDate>Fri, 24 Nov 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>RSS autodiscover on your blog</title>
                <description>
&lt;p&gt;Last week I read an interesting article about why RSS was still so useful in 2017, and it reminded me how much I was using RSS feeds and aggregators a few years ago.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;fr&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;RSS: there&amp;#39;s nothing better&amp;quot; - or why old geeky things are still very useful for everyone&lt;a href=&quot;https://t.co/2tW6998dg2&quot;&gt;https://t.co/2tW6998dg2&lt;/a&gt;&lt;/p&gt;&amp;mdash; Suixo (@msuixo) &lt;a href=&quot;https://twitter.com/msuixo/status/929297267261083648?ref_src=twsrc%5Etfw&quot;&gt;11 novembre 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;But last week, Mozilla also released Firefox Quantum with a built-in “Subscribe to this page” RSS button (I suspect it had been there for a very long time and I just didn’t see it before).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/rss.png&quot; alt=&quot;RSS autodiscover feature&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And then, horror! The RSS feed for my blog is not detected! … Oh ok I need to add something. Easy.&lt;/p&gt;

&lt;p&gt;To signal to browsers such as Firefox that &lt;strong&gt;your blog or your website has a RSS feed they can subscribe to&lt;/strong&gt;, you need to add the following code (in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alternate&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;application/rss+xml&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;title=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;My awesome blog&apos;s RSS feed&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/rss.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here you go, the button now allows your users to subscribe to your feed. Aggregator will also now “see” your content and make it discoverable to other readers.&lt;/p&gt;

&lt;p&gt;You can also add multiple tags if you have multiple feeds.&lt;/p&gt;

&lt;p&gt;That was a quick fix to a problem many website have today, so if you haven’t already, add this nice little chunk of code to your pages :)&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2017/11/20/rss-autodiscover-on-your-blog/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2017/11/20/rss-autodiscover-on-your-blog</guid>
                <pubDate>Mon, 20 Nov 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Pipe tar through SSH to transfer large files</title>
                <description>
&lt;h2 id=&quot;using-tar-and-ssh-to-efficiently-transfer-large-files-across-hosts&quot;&gt;Using tar and ssh to efficiently transfer large files across hosts&lt;/h2&gt;

&lt;p&gt;Ever ran out of disk space to create a (compressed) archive of a folder you want to backup or transfer to another host?&lt;/p&gt;

&lt;p&gt;Here is the solution: &lt;strong&gt;directly pipe the output of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tar&lt;/code&gt;&lt;/strong&gt; (used to create archives of files and folders, and also often used to compress this final archive) &lt;strong&gt;through ssh&lt;/strong&gt; to retrieve the compressed archive on another host. This allow you to backup large volumes of data when the source host doesn’t have enough space to store the original data + a compressed archive of the data.&lt;/p&gt;

&lt;p&gt;From the distant host (the host you want to copy the backup to):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Execute this on the host that will receive the data from the source server
ssh user@source-server &quot;tar czpf - /some/important/data /some/other/file/or/folder&quot; | tar xzpf - -C /backup/destination/folder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will copy the two files/folders into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/backup/destination/folder&lt;/code&gt; on the destination host (the one executing the command).&lt;/p&gt;

&lt;p&gt;If on the other hand, you want to &lt;strong&gt;send&lt;/strong&gt; the data to another server and you are executing the SSH commands from the source server, use the following command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Execute this on the machine with the data available locally
tar cpf - /some/important/data /some/other/file/or/folder | ssh user@destination-server &quot;tar xpf - -C /backup/destination/folder/&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2017/10/14/pipe-tar-through-ssh-to-transfer-large-files/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2017/10/14/pipe-tar-through-ssh-to-transfer-large-files</guid>
                <pubDate>Sat, 14 Oct 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>DNS CAA - A (not so) new mechanism for a better PKI</title>
                <description>
&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: DNS CAA is a DNS record allowing domain owners to restrict which CA
are allowed to issue certificates for their domain. While it is a useful
additional security feature, it does not protect against unwilling CAs nor
mis-issued certificate being used in the wild. CA must implement it before
September 2017.&lt;/p&gt;

&lt;p&gt;A few days ago, I discovered &lt;strong&gt;DNS CAA&lt;/strong&gt; (which stands for &lt;em&gt;DNS Certification
Authority Authorization&lt;/em&gt;) and wanted to get it a try. Here is a short write-up
about what I learned, and the steps if you want to protect your domain with DNS
CAA too.&lt;/p&gt;

&lt;p&gt;While quite old (2013), this standard will &lt;a href=&quot;https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum&quot;&gt;become mandatory in
September&lt;/a&gt;
and all CA will have to implement it or face sanctions.&lt;/p&gt;

&lt;h2 id=&quot;a-new-dns-record&quot;&gt;A new DNS record&lt;/h2&gt;

&lt;p&gt;DNS CAA is actually a new DNS record (the “DNS CAA Resource Record” or DNS CAA
RR for the folks who love acronyms). &lt;a href=&quot;https://tools.ietf.org/html/rfc6844&quot;&gt;RFC
6844&lt;/a&gt; explains it role:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Certification Authority Authorization (CAA) DNS Resource Record
   allows a DNS domain name holder to specify one or more Certification
   Authorities (CAs) authorized to issue certificates for that domain.
   CAA Resource Records allow a public Certification Authority to
   implement additional controls to reduce the risk of unintended
   certificate mis-issue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, it allows us - domain owners - to specify &lt;strong&gt;what CAs we are allowing to deliver certificates for our domain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Until now, any CA can deliver a certificate for any domain name. It is a known
weakness of the traditional web PKI, and we have seen many examples of CA
abusing their power to deliver counterfeit certificates (&lt;a href=&quot;https://arstechnica.com/security/2017/03/google-takes-symantec-to-the-woodshed-for-mis-issuing-30000-https-certs/&quot;&gt;the most recent one
being
Symantec&lt;/a&gt;,
but also
&lt;a href=&quot;http://www.h-online.com/security/news/item/Trustwave-issued-a-man-in-the-middle-certificate-1429982.html&quot;&gt;Trustwave&lt;/a&gt;,
among others). &lt;a href=&quot;https://nakedsecurity.sophos.com/2013/01/08/the-turktrust-ssl-certificate-fiasco-what-happened-and-what-happens-next/&quot;&gt;Some even didn’t issued these certificates on
purpose&lt;/a&gt;
(sic).&lt;/p&gt;

&lt;p&gt;Please note that some CAs mis-issuing certificates had actually been
compromised: Comodo and
&lt;a href=&quot;https://arstechnica.com/security/2011/03/independent-iranian-hacker-claims-responsibility-for-comodo-hack/&quot;&gt;Diginotar&lt;/a&gt;,
&lt;a href=&quot;https://www.cert.org/historical/advisories/CA-2001-04.cfm&quot;&gt;Verisign&lt;/a&gt;… The new
DNS CAA Resource Record aims at preventing the first class of these issues:
non-compromised CA delivering certificates to the wrong person. DNS CAA will
have no effect if the CA is controlled by a malicious actor.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;Aside note: did you know the first checks restricting a CA’s ability to deliver certificates
were written after… the French National CyberSecurity Agency (ANSSI) kind of
(&lt;a href=&quot;https://arstechnica.com/security/2013/12/french-agency-caught-minting-ssl-certificates-impersonating-google/&quot;&gt;the whole story is bit more
complicated&lt;/a&gt;)
delivered a certificate for “google.com”. Of course, it wasn’t officially
allowed to do it. So now, if you search for “ANSSI” in Mozilla’s source code,
you will find &lt;a href=&quot;https://hg.mozilla.org/mozilla-central/rev/218997c808a1#l1.34&quot;&gt;this
diff&lt;/a&gt; explicitely
stating that any certificate delivered by the ANSSI is only valid for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.fr&lt;/code&gt;
domains, or a couple of other French-related TLDs. I find it pretty amazing to
have a hardcoded constraint in a browser just for my country’s agency.&lt;/p&gt;

&lt;p&gt;Unfortunately, this kind of event will still be possible with DNS CAA. More on
this in the next parts, but first, &lt;strong&gt;what is this brand new DNS record?&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-dns-ca-rr&quot;&gt;The DNS CA RR&lt;/h3&gt;

&lt;p&gt;A DNS CA RR is composed of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;some flags (currently just a “critical” flag if 128, otherwise value is 0)&lt;/li&gt;
  &lt;li&gt;a tag name (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;issue&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;issuewild&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iodef&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;a tag value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thus, the following record states that &lt;em&gt;only Lets’Encrypt is allowed to deliver certificates for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.com&lt;/code&gt;&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;example.com.  CAA 0 issue &quot;letsencrypt.org&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is possible to allow multiple CAs for a same domain:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;example.com.  CAA 0 issue &quot;comodoca.com&quot;
example.com.  CAA 0 issue &quot;letsencrypt.org&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;issuewild&lt;/code&gt; says the CA is allowed to issue wildcard certs:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;example.com.  CAA 0 issue &quot;letsencrypt.org&quot;
example.com.  CAA 0 issuewild &quot;comodoca.com&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iodef&lt;/code&gt; is used to get notified of policy violations:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;example.com.  CAA 0 iodef &quot;mailto:example@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For now, I don’t know of any CA supporting this directive, but it is a good practice to use one so that you are prepared.&lt;/p&gt;

&lt;h3 id=&quot;dns-caa-only-affects-the-certificate-generation-process&quot;&gt;DNS CAA only affects the certificate generation process&lt;/h3&gt;

&lt;p&gt;From the RFC:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Before issuing a certificate, a compliant CA MUST check for
   publication of a relevant CAA Resource Record set.  If such a record
   set exists, a CA MUST NOT issue a certificate unless the CA
   determines that either (1) the certificate request is consistent with
   the applicable CAA Resource Record set or (2) an exception specified
   in the relevant Certificate Policy or Certification Practices
   Statement applies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the RR is &lt;strong&gt;only checked at certificate issuance time&lt;/strong&gt;, but &lt;strong&gt;not at “runtime”&lt;/strong&gt; in the browser, when a TLS connection is established.&lt;/p&gt;

&lt;p&gt;This is important: DNS CAA will not protect you from the malicious CAs issuing
certificates for your domain (not like the NSA actually needed it). If you are
looking for this kind of protection, look at
&lt;a href=&quot;https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities&quot;&gt;DANE&lt;/a&gt;
(binding a x509 certificate to the DNS records) and
&lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning&quot;&gt;HPKP&lt;/a&gt; (HTTP Public Key
Pinning, basicaly the same idea but through a HTTP header).&lt;/p&gt;

&lt;h2 id=&quot;do-it-yourself&quot;&gt;Do it yourself&lt;/h2&gt;

&lt;p&gt;Unlike HPKP that can be pretty tragic if misconfigured, you can safely reproduce
the following DNS CAA steps at home. The only inconvenience you may experience is an
administrative one while trying to renew your certificates, and updating your
DNS records will get you safe while not affecting your uptime or your visitors.&lt;/p&gt;

&lt;h3 id=&quot;read-and-check-dns-caa-records&quot;&gt;Read and check DNS CAA records&lt;/h3&gt;

&lt;p&gt;Before adding our own records, let’s check out how Google is using the CAA RR:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;dig +nocomments type257 google.com
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &amp;lt;&amp;lt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; DiG 9.10.4-P5 &amp;lt;&amp;lt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; +nocomments type257 google.com
&lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt; global options: +cmd
&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;google.com.                    IN      CAA
google.com.             86399   IN      CAA     0 issue &lt;span class=&quot;s2&quot;&gt;&quot;pki.goog&quot;&lt;/span&gt;
google.com.             86399   IN      CAA     0 issue &lt;span class=&quot;s2&quot;&gt;&quot;symantec.com&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dig&lt;/code&gt; does not (yet) support this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CAA&lt;/code&gt; RR, so we have to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;type257&lt;/code&gt; instead.
Here we learn that only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pki.goog&lt;/code&gt; and Symantec are allowed to deliver
certificates for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.com&lt;/code&gt; (and no one is allowed to deliver wildcard certs).&lt;/p&gt;

&lt;p&gt;Fun fact: Google seems to be still experimenting with this new standard as domain names like “google.fr” are not (yet?) returning CAA records.&lt;/p&gt;

&lt;h3 id=&quot;add-your-own-records&quot;&gt;Add your own records&lt;/h3&gt;

&lt;p&gt;Now, let’s add you own records. Let’s say you are using &lt;strong&gt;Let’s Encrypt&lt;/strong&gt; and
you want to protect your domain &lt;strong&gt;blog.securem.eu&lt;/strong&gt;. Only Let’s Encrypt should be
allowed to deliver certificates for this domain. You also want to be notified if
there is a &lt;strong&gt;policy violation&lt;/strong&gt; (if someone tries to register blog.securem.eu on
GoDaddy for instance), at your email elon.musk@gmail.com (lucky you!). Here are the records
you need to add to your DNS zone file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-dns&quot;&gt;blog.securem.eu.	IN	CAA	0 issue &quot;letsencrypt.org&quot;
blog.securem.eu.	IN	CAA	0 iodef &quot;mailto:elon.musk@gmail.com&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Beware: not all registrars currently support CAA records, you may try
&lt;a href=&quot;https://sslmate.com/labs/caa/&quot;&gt;https://sslmate.com/labs/caa/&lt;/a&gt; with the “legacy
mode” to make it work.&lt;/p&gt;

&lt;h3 id=&quot;did-you-do-well&quot;&gt;Did you do well?&lt;/h3&gt;

&lt;p&gt;Tools such as &lt;a href=&quot;https://github.com/drwetter/testssl.sh&quot;&gt;test.ssl&lt;/a&gt; or the &lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot;&gt;Qualys
Server Test&lt;/a&gt; now check these records, and you
should now get a green result, yipeee!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/caa-dns-google.png&quot; alt=&quot;Example of Google&apos;s Qualy test results, as of July 3rd&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;take-away&quot;&gt;Take-away&lt;/h2&gt;

&lt;p&gt;While it is not yet supported by all the providers, this 4-year-old standard has
&lt;a href=&quot;https://blog.qualys.com/ssllabs/2017/03/13/caa-mandated-by-cabrowser-forum&quot;&gt;recently become mandatory for
CAs&lt;/a&gt;.
Thus, &lt;strong&gt;all of them will have to implement CAA verification by September&lt;/strong&gt;, so
be ready and add your own records today!&lt;/p&gt;

&lt;p&gt;Please also note that this standard is &lt;strong&gt;not bulletproof&lt;/strong&gt; but will
&lt;strong&gt;contribute&lt;/strong&gt; to your overall security.&lt;/p&gt;

&lt;p&gt;I will be more than happy to answer your questions, comments, and thoughts about
this “new” mechanism! :)&lt;/p&gt;

&lt;p&gt;Feel free to comment on the &lt;a href=&quot;https://news.ycombinator.com/item?id=15156106&quot;&gt;Hacker News conversation&lt;/a&gt; or in the Discuss below.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2017/07/02/dns-caa-what-why-and-how-this-new-pki-control-mechanism/</link>
                <guid>https://blog.securem.eu/serverside/2017/07/02/dns-caa---what-why-and-how-this-new-pki-control-mechanism</guid>
                <pubDate>Sun, 02 Jul 2017 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>How I renew my TLS certificates automatically thanks to Let&apos;s Encrypt</title>
                <description>
&lt;p&gt;&lt;em&gt;TL;DR&lt;/em&gt;: this post explains how my VPS automatically renew its HTTPS
certificates, by using Let’s Encrypt and a simple crontab.&lt;/p&gt;

&lt;h2 id=&quot;you-said-lets-encrypt&quot;&gt;You said Let’s Encrypt?&lt;/h2&gt;

&lt;p&gt;First of all, you may not have heard of &lt;strong&gt;Let’s Encrypt&lt;/strong&gt;, which is a &lt;em&gt;free,
automated, and open certificate authority (CA), run for the public’s benefit&lt;/em&gt;.
One of its goal is to make the use of a SSL/TLS certificate (that gives you the
HTTPS) more widespread, mainly by providing users with &lt;strong&gt;free certificates&lt;/strong&gt; and
in a &lt;strong&gt;very simple way&lt;/strong&gt;: all you have to do is to install their tool
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;letsencrypt&lt;/code&gt; on your server and then to run one command to retrieve and install
the certificate.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/le-logo-wide.png&quot; alt=&quot;Let&apos;s Encrypt gives you free certificates in a very simple way&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Well, this is the theory, and the script is still in beta so it may require a
bit of tweaking to actually receive your certificate, but it is still a very
easy task.&lt;/p&gt;

&lt;p&gt;To actually give you the certificate you need and to prevent you from asking a
certificate for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google.com&lt;/code&gt;, Let’s Encrypt checks that the server running the
script does receive traffic for the domain name you specified. If I want to get
a certificate for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lab.securem.eu&lt;/code&gt;, Let’s Encrypt will check that I do receive
the HTTP(S) requests for lab.securem.eu.&lt;/p&gt;

&lt;p&gt;In my case, I have an OpenVPN server running on port 443, and I configured it
with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;port-share&lt;/code&gt; parameter that allows the non-OpenVPN traffic to be
forwarded to another address/port. My Apache is also listening on the port 444
so I forward all the non-OpenVPN traffic on port 443 to this alternative port. I
am not quite sure this is the reason of Let’s Encrypt having problems but it
most certainly plays a role.&lt;/p&gt;

&lt;h2 id=&quot;get-your-first-certificate&quot;&gt;Get your first certificate&lt;/h2&gt;

&lt;p&gt;To start, you can try the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;standalone&lt;/code&gt; mode that will run a little server
locally, and listen on the port 80 or 443 for the challenges. Let’s choose the
port 443, because we do not have yet any HTTPS-enabled website.&lt;/p&gt;

&lt;p&gt;First, install the tool:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto --help
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s retrieve your certificate! We will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;standalone&lt;/code&gt; mode, so you will
have to stop all the services listening on port 443. In my case:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo service apache2 stop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, just run one command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./letsencrypt-auto certonly --standalone --standalone-supported-challenge tls-sni-01
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http-01&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls-sni-01&lt;/code&gt; if you prefer to bind to port 80.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/LetsEncryptStandalone.png&quot; alt=&quot;The script will ask you the domain(s) you want to register&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Just enter the domain(s) the server owns and for which you want a certificate.
&lt;strong&gt;Et voilà!&lt;/strong&gt;, you got the appropriate certificate! Any additional domains will
be contained in the certificate using the SNI extension (Server Name Indication)
that allows one server and one certificate to respond to multiple HTTPS-enabled
domains with no name clash.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/LetsEncryptSuccess.png&quot; alt=&quot;The certificate is now available&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now, all you have to do is to configure your web server (or whatever will use
the certicate) and point it to the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/letsencrypt/live/your.domain.here/fullchain.pem&lt;/code&gt; public certificate file.
The private key is placed in the same folder, as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;privkey.pem&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This process is &lt;strong&gt;really fast and easy compared to other CA&lt;/strong&gt; where you have to
send documents by mail or at least to wait for a few days.&lt;/p&gt;

&lt;p&gt;One thing you may have noticed is that the certificates are &lt;strong&gt;only valid for
three months&lt;/strong&gt;. This is &lt;a href=&quot;https://letsencrypt.org/2015/11/09/why-90-days.html&quot;&gt;done on
purpose&lt;/a&gt;, for security
reasons (in case of key compromise and mis-issuance) and to encourage
automation. This means that in three months you will receive an email saying
that your domains are close to the expiry date and that you should renew them as
soon as possible.&lt;/p&gt;

&lt;h2 id=&quot;automatically-renewing-the-certificates-using-the-webroot-method&quot;&gt;Automatically renewing the certificates using the webroot method&lt;/h2&gt;

&lt;p&gt;After manually renewing you certificates the first two times, you will begin to
say “Hey, I will definitely automate this process!”. Lucky you, Let’s Encrypt is
really easy to automate.&lt;/p&gt;

&lt;p&gt;If we automate it, we won’t be using the standalone server as we don’t want to
stop and start the Apache daemon with no user interaction (and because we don’t
like downtime). &lt;strong&gt;If you are lucky, you may be able to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apache&lt;/code&gt; or even
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; plugins that automate everything for you&lt;/strong&gt;. If you are not, like me,
give a try to &lt;strong&gt;the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;webroot&lt;/code&gt; method&lt;/strong&gt;. With this method, Let’s Encrypt will try
to access a file inside a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known&lt;/code&gt; directory at the root of your website.
In my case, it will try to access a file at
http://kikoo.securem.eu/.well-known/letsencrypt-challenge-something. This method
allows to keep your website running for every other endpoints on
http://kikoo.securem.eu/.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First step&lt;/strong&gt;: configure you virtualhost to redirect the requests on
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/letsencrypt-webroot/.well-known&lt;/code&gt;. For Apache, all you
need is an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Alias&lt;/code&gt; directive (don’t forget to reload the configuration after
that):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Alias /.well-known/ /var/www/letsencrypt-webroot/.well-known/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Second step&lt;/strong&gt;: Run the command a first time to ensure everything works fine:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./letsencrypt-auto certonly -d your.expiring.domain.com --webroot --webroot-path /var/www/letsencrypt-webroot/ --non-interactive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I chose to put then renew command in a crontab, ran as root as it needs to
access protected parts of the system. It is ran every month at 1am.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo crontab -e
# This should open an editor, so add the following line
0 1 1 * * /where/you/installed/letsencrypt/letsencrypt-auto certonly -d your.expiring.domain.com --webroot --webroot-path /var/www/letsencrypt-webroot/ --non-interactive &amp;amp;&amp;amp; nginx -s reload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One important thing is that &lt;strong&gt;you need to reload your webserver&lt;/strong&gt; (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp; nginx
-s reload&lt;/code&gt; part) to make it use the new certificate: without that it will
continue using the old one. Thanks to Justin Finkelstein in the comments for
pointing it out.&lt;/p&gt;

&lt;p&gt;Now you can let your server manage its own certificates :)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/robot-automation.gif&quot; alt=&quot;Now just hope this will work&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I hope this article will be useful to you, let me know how YOU proceed to renew
your certificates!&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2016/04/27/how-i-renew-my-tls-certificates-automatically-thanks-to-lets-encrypt/</link>
                <guid>https://blog.securem.eu/serverside/2016/04/27/how-i-renew-my-tls-certificates-automatically-thanks-to-lets-encrypt</guid>
                <pubDate>Wed, 27 Apr 2016 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Creating and publishing a Python package</title>
                <description>
&lt;p&gt;In this post I explain how to transform a working code into a &lt;strong&gt;python package&lt;/strong&gt;
that can be &lt;strong&gt;installed from PyPi&lt;/strong&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install yourmodule&lt;/code&gt;. I will
suppose you are modern and use &lt;strong&gt;Python 3&lt;/strong&gt;. For those in a hurry, three steps:
clone a repository, update &lt;a href=&quot;#configure-the-name-version-description&quot;&gt;the setup.py
file&lt;/a&gt;, and follow the instructions &lt;a href=&quot;#in-a-nutshell&quot;&gt;in
the last part&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;quick-reminder-on-python-modules-and-packages&quot;&gt;Quick reminder on Python modules and packages&lt;/h2&gt;

&lt;p&gt;Creating a Python module is easy:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Create a file with your code, call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myhellomodule.py&lt;/code&gt; (or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whateveriwant.py&lt;/code&gt;):&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;def hello():
    print(&quot;I am a Python3 module!&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This a &lt;strong&gt;module&lt;/strong&gt;: a file with things in it. You can import it or do
 whatever you want.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Make it a package:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;touch __init__.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And ta-daaa you have a Python &lt;strong&gt;package&lt;/strong&gt;. If the directory containing the two
files (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myhellomodule.py&lt;/code&gt;) is called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swaggymodule&lt;/code&gt;, then you
can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;from swaggymodule.myhellomodule import hello&lt;/code&gt; (from the parent directory).&lt;/p&gt;

&lt;p&gt;Nothing fancy for now. Let’s see how to publish such a wonderful package, so
that your grandmother can install it with a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install swaggymodule&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;packaging-your-project&quot;&gt;Packaging your project&lt;/h2&gt;

&lt;p&gt;First, clone the sample project and give it the name of your module:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/pypa/sampleproject swaggymodule
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The important files:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;setup.py&lt;/strong&gt; allows you to specify your project &lt;strong&gt;configuration&lt;/strong&gt;, and to run
 packaging commands: try &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python setup.py --help-commands&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;setup.cfg&lt;/strong&gt; is an INI file containing option defaults for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt;
 commands&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;README.rst&lt;/strong&gt; describes the goal of the project, using &lt;a href=&quot;http://docutils.sourceforge.net/rst.html&quot;&gt;reStructuredText&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy your module(s) inside this new folder and remove the existing “sample”
module:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;swaggymodule
|- swaggymodule
  |- __init__.py
  |- myhellomodule.py
|- setup.py
|- setup.cfg
|- README.rst
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;configure-the-name-version-description&quot;&gt;Configure the name, version, description…&lt;/h3&gt;

&lt;p&gt;First, open the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; file, to enter a few details about the project. All the fields are self-explanatory but here are a few additional pieces of advice:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: according to PEP0008:&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;Never use dashes (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;) in your package name because importing it would
  become a &lt;a href=&quot;http://stackoverflow.com/questions/8350853/how-to-import-python-module-when-module-name-has-a-dash-or-hyphen-in-it&quot;&gt;very hard task&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;license&lt;/strong&gt;: in case of doubt (what should I use?), try
 &lt;a href=&quot;http://choosealicense.com/&quot;&gt;http://choosealicense.com/&lt;/a&gt;: it’s super clear.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;classifiers&lt;/strong&gt;: check &lt;a href=&quot;https://pypi.python.org/pypi?%3Aaction=list_classifiers&quot;&gt;https://pypi.python.org/pypi?%3Aaction=list_classifiers&lt;/a&gt;
 for a full list.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;entry_points&lt;/strong&gt;: in case your application plugs in an existing system, you may specify an entry point. &lt;a href=&quot;http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins&quot;&gt;See this page for more details&lt;/a&gt;. But in most cases you should just set it to an empty dictionnary.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;the-actual-packaging-steps&quot;&gt;The actual packaging steps&lt;/h3&gt;

&lt;p&gt;So that you don’t run into the same issues as me:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install wheel twine setuptools --upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;First, create a &lt;em&gt;Source Distribution&lt;/em&gt;. This kind of “distribution” (aka
“Package”) requires a build step when installed by pip.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python setup.py sdist
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But we want to install a “wheel” (a built package) which is faster to install
than a source distribution:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python setup.py bdist_wheel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;uploading-your-package&quot;&gt;Uploading your package&lt;/h2&gt;

&lt;p&gt;You will need &lt;a href=&quot;https://pypi.python.org/pypi?%3Aaction=register_form&quot;&gt;an account on
PyPi&lt;/a&gt;. But also create one
on the test platform we will use:
&lt;a href=&quot;https://testpypi.python.org/pypi?%3Aaction=register_form&quot;&gt;testpypi.python.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The official documentation recommends to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine&lt;/code&gt; instead of the default
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python setup.py upload&lt;/code&gt; because the latter uploads files and credentials over
plain HTTP whereas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine&lt;/code&gt; uses TLS (so use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twine&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;To save the configuration data, create a file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.pypirc&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[distutils]
index-servers=pypi

[pypi]
repository = https://testpypi.python.org/pypi
username = yourusername
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may also add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;password&lt;/code&gt; line if you don’t want to type your password
everytime. Then:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Register your project&lt;/span&gt;
twine register dist/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.whl
&lt;span class=&quot;c&quot;&gt;# Upload the project&lt;/span&gt;
twine upload dist/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And this is what you should get when you head to
&lt;a href=&quot;https://testpypi.python.org/pypi&quot;&gt;testpypi.python.org&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/pypitest-page.png&quot; alt=&quot;screenshot from PyPi Test&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you need to make updates: change your code, bump the version to 1.0.1 in the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; file, optionally commit your code to your repository, and do:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;twine upload dist/swaggymodule-1.0.1&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should have updated your project online and made the new version available.
Now try it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ pip install -i https://testpypi.python.org/pypi swaggymodule
Downloading/unpacking swaggymodule
  Downloading swaggymodule-1.0.1-py3-none-any.whl
  ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;It works!&lt;/strong&gt; But before letting people know about your new package, we should
upload it to the actual PyPi (not the test one). The process is summed up in
the next section:&lt;/p&gt;

&lt;h2 id=&quot;in-a-nutshell&quot;&gt;In a nutshell&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;wheel twine setuptools &lt;span class=&quot;nt&quot;&gt;--upgrade&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Check and update the server to use (https://pypi.python.org/pypi) and your credentials&lt;/span&gt;
nano ~/.pypirc
&lt;span class=&quot;c&quot;&gt;# Build the distributions&lt;/span&gt;
python setup.py sdist
python setup.py bdist_wheel
&lt;span class=&quot;c&quot;&gt;# Register and upload your package&lt;/span&gt;
twine register dist/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.whl
twine upload dist/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;to-go-further&quot;&gt;To go further&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://packaging.python.org/en/latest/distributing/&quot;&gt;The official tutorial&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.python.org/dev/peps/pep-0008/#package-and-module-names&quot;&gt;The naming convention&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A more complete article: &lt;a href=&quot;https://www.jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/&quot;&gt;Open Sourcing a Python Project the
Right Way&lt;/a&gt;
by Jeff Knup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following these steps, I was able to publish a &lt;a href=&quot;https://pypi.python.org/pypi/django-generate-secret-key&quot;&gt;small
application&lt;/a&gt; we were
using on a closed-source project, to share it but also to decrease the amount of
code inside the project itself that &lt;em&gt;was not focused on the business logic&lt;/em&gt;.
This application is a &lt;a href=&quot;https://pypi.python.org/pypi/django-generate-secret-key&quot;&gt;Django application enabling users to generate a new
secret key when deploying their Django
project&lt;/a&gt;. In our
project, a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install django-generate-secret-key&lt;/code&gt; replaced a whole
folder and saved us space, complexity, and increased the readability of the
source code.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2016/02/29/creating-and-publishing-a-python-module/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2016/02/29/creating-and-publishing-a-python-module</guid>
                <pubDate>Mon, 29 Feb 2016 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Recording a timelapse with a Raspberry Pi</title>
                <description>
&lt;p&gt;&lt;em&gt;TLDR&lt;/em&gt;: this post explains how to use your &lt;em&gt;Raspberry Pi&lt;/em&gt; to record pictures and
then assemble them into a video with &lt;em&gt;ffmpeg&lt;/em&gt; to produce a &lt;em&gt;timelapse&lt;/em&gt; video.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/raspberry-pi-camera.jpg&quot; alt=&quot;The camera of my Raspberry Pi&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yesterday I had a party at home, and I chose to record the fun of the party with
my brand new Raspberry Pi. I had bought a camera with the Pi, the model without
the infrared filter (that &lt;em&gt;blocks&lt;/em&gt; infrared) called the NoIR Camera. This camera
basically allows you to “see” the infrared light - that humans cannot see - used
for TV remotes, Wiimotes, fingertracking or… night vision. Useless today, it
just produced weird colors (blue / pink shades) on the pictures, so you may
better use the standard camera (that blocks IR).&lt;/p&gt;

&lt;h2 id=&quot;setting-up-the-raspberry-pi-and-the-camera&quot;&gt;Setting up the Raspberry Pi and the camera&lt;/h2&gt;

&lt;p&gt;Several third-party tools allow you to do a timelapse with a Pi, with a lot of
options and features, but I chose to stick with a more simple tool already
present on the Pi: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspistill&lt;/code&gt;. Along with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspivid&lt;/code&gt;, these tools allow you to
play with your camera.&lt;/p&gt;

&lt;p&gt;First of all, plug your camera. You may need to &lt;a href=&quot;https://www.raspberrypi.org/documentation/configuration/camera.md&quot;&gt;activate the support for the
camera with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspi-config&lt;/code&gt;&lt;/a&gt;
if you never did it before. I had a permission issue because I was not using the
default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pi&lt;/code&gt; user and thus was not in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;video&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;We can take a picture with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspistill&lt;/code&gt; (&lt;a href=&quot;https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspistill.md&quot;&gt;see the documentation if
needed&lt;/a&gt;).
Let’s try it now to check if the camera does see us. Maybe we will need to flip
the image or move the camera.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;raspistill -o cam.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I am using a headless Pi (without graphical server) with SSH so I had to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp
pi:cam.jpg .&lt;/code&gt; to retrieve the picture and check the result.&lt;/p&gt;

&lt;h2 id=&quot;start-the-capture&quot;&gt;Start the capture&lt;/h2&gt;

&lt;p&gt;Then, now that your camera is placed well and you are ready to start the
capture, all you have to do is save an image every N seconds:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;raspistill -t 0 -tl 10000 -n -w 1920 -h 1080 -o image%04d.jpg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raspistill&lt;/code&gt; has a &lt;strong&gt;built-in feature&lt;/strong&gt; to do the timelapse:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-tl T&lt;/code&gt;: a picture will be taken every T millisecond (here 10s)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t 0&lt;/code&gt;: never timeout, keep shooting. You may want to set it to a high value like a few hours (value in ms)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt;: do not show any preview on the HDMI screen (anyway we have no screen)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-w 1920 -h 1080&lt;/code&gt;: HD is sufficient for a timelapse, you don’t want a final 4GB video! Default is 2592 x 1944 (5 megapixels)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-o image%04d.jpg&lt;/code&gt;: filename for saved files, here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%04d&lt;/code&gt; means the number of the picture, on 4 digits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the output format, you can replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%04d&lt;/code&gt; by a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%d&lt;/code&gt; and add the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-dt&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--datetime&lt;/code&gt;) flag that will replace the frame number by the datetime
(MonthDayHourMinSec), to produce &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test221172225.jpg&lt;/code&gt; (February 21st, 17:22:25).
If needed, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-dt&lt;/code&gt; flag can be replaced by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ts&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--timestamp&lt;/code&gt;) that will use
the number of seconds since 1900.&lt;/p&gt;

&lt;p&gt;Using the initial format, you will soon see a large number of files like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image0012.jpg&lt;/code&gt;, that can take up a large disk space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What delay for what storage space?&lt;/strong&gt;
I chose to take a picture (~3MB) every ~20s, which is ~9MB every minute, 520MB
every hour. I got a nice 4,7GB for 9h of time. Plan carefully your timelapse if
you do not have much space on your SD card.&lt;/p&gt;

&lt;h2 id=&quot;assembling-the-pictures-into-a-video&quot;&gt;Assembling the pictures into a video&lt;/h2&gt;

&lt;p&gt;First, move the pictures to your computer, to benefit from a higher processing
power (needed for video edition). You can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rsync&lt;/code&gt;, but I recommend to
just insert your SD card into your computer (way more quick and easy).&lt;/p&gt;

&lt;p&gt;Some tutorials suggest using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mencoder&lt;/code&gt; but this package is not available on my
Ubuntu. I thus used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Assemble the pictures (this step is I/O intensive, you may want to use your SSD), at a rate of 25 pictures per second:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg -r 25 -pattern_type glob -i &apos;*.jpg&apos; -c:v copy output.avi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Convert your video to a smaller format (this step is CPU intensive):&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ffmpeg -i output.avi -c:v libx264 -preset slow -crf 15 output-final.mkv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And TADAAA your timelapse video is ready! :D&lt;/p&gt;

&lt;p&gt;Now, let’s laugh a bit seeing the potato chips bowl magically emptying, the
glasses moving, people dancing, playing games, kissing or falling asleep.
And you can even create GIFs of your funny friends using the raw pictures!&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/projects/2016/02/21/recording-a-timelapse-with-a-raspberry-pi/</link>
                <guid>https://blog.securem.eu/projects/2016/02/21/recording-a-timelapse-with-a-raspberry-pi</guid>
                <pubDate>Sun, 21 Feb 2016 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Setting up an ownCloud Server in a Docker container using Docker Compose</title>
                <description>
&lt;p&gt;&lt;em&gt;TLDR&lt;/em&gt;: this post explains how to use &lt;em&gt;Docker&lt;/em&gt; to setup an &lt;em&gt;ownCloud server&lt;/em&gt; with a PostgreSQL database, persisting the data
across reboots or image upgrades.&lt;/p&gt;

&lt;p&gt;I finally reinstalled my entire VPS, using the provisioning tool Ansible,
and I wanted to have a more isolated install of &lt;a href=&quot;https://owncloud.org/&quot;&gt;ownCloud&lt;/a&gt;.
I decided to use a &lt;em&gt;Docker container&lt;/em&gt; to run this application in a more &lt;em&gt;isolated&lt;/em&gt; and &lt;em&gt;portable&lt;/em&gt; way,
and to avoid installing this application directly on the server.&lt;/p&gt;

&lt;h2 id=&quot;so-i-used-docker&quot;&gt;So I used Docker&lt;/h2&gt;

&lt;p&gt;First question, what image to use? When typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owncloud&lt;/code&gt; on &lt;a href=&quot;https://hub.docker.com/search/?q=owncloud&amp;amp;page=1&amp;amp;isAutomated=0&amp;amp;isOfficial=0&amp;amp;starCount=0&amp;amp;pullCount=0&quot;&gt;DockerHub&lt;/a&gt;,
I immediately see there is &lt;a href=&quot;https://hub.docker.com/_/owncloud/&quot;&gt;a (recent) &lt;em&gt;official image&lt;/em&gt;&lt;/a&gt;, simply named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owncloud&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can try to run it right now to check everything works fine:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run --rm -it --name owncloud -p 80:80 owncloud
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/docker-owncloud-1.png&quot; alt=&quot;Screenshot of the first run page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That was quick. Now, you can exit/stop this container.&lt;/p&gt;

&lt;h3 id=&quot;we-will-need-a-database&quot;&gt;We will need a database&lt;/h3&gt;

&lt;p&gt;If you don’t already have a database up and running you want to use,
 you can either install one on the host, or &lt;em&gt;spin up a second container&lt;/em&gt; for it.
Being an adept of PostgreSQL, let’s use &lt;a href=&quot;https://hub.docker.com/_/postgres/&quot;&gt;the associate container&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run --name owncloud-postgres -e POSTGRES_PASSWORD=mysecretpassword -d postgres
docker run --rm -it --link owncloud-postgres:owncloud-db --name owncloud -p 80:80 owncloud
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead of a SQLite database, we use a &lt;em&gt;PostgreSQL&lt;/em&gt; one, with user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt;,
 the password supplied when creating the container (here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysecretpassword&lt;/code&gt;),
 the database &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt;, and the host &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owncloud-db&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should be able to get a running instance by entering these settings in the startup page of ownCloud.
If it doesn’t work you can see the debug log (created after the first request on owncloud), using:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker exec -it owncloud tail -f /var/www/html/data/owncloud.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;persisting-the-data-across-container-destruction-and-upgrade&quot;&gt;Persisting the data across container destruction and upgrade&lt;/h3&gt;

&lt;p&gt;We will need to &lt;em&gt;store the ownCloud data and configuration folders outside of the container&lt;/em&gt;,
as upgrading the container would destroy everything.&lt;/p&gt;

&lt;p&gt;This can be achieved by using Docker Volumes, and even &lt;em&gt;&lt;a href=&quot;https://docs.docker.com/userguide/dockervolumes/#creating-and-mounting-a-data-volume-container&quot;&gt;docker data volumes&lt;/a&gt;&lt;/em&gt;.
We will need to do the same for the &lt;em&gt;PostgreSQL container&lt;/em&gt; (think “database backup”).&lt;/p&gt;

&lt;p&gt;At this point, copy-pasting the commands to run these containers begins to be unreadable,
 and painful. What we want is to describe how containers should be started, linked, and
configured, so let’s use &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;docker-compose&quot;&gt;Docker Compose&lt;/h2&gt;

&lt;p&gt;The concept is simple: &lt;em&gt;use a YAML file to describe how your containers should be… composed
together&lt;/em&gt;, and then let a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; start everything!&lt;/p&gt;

&lt;p&gt;You can install it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install docker-compose&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Composition of the containers needed&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;owncloud&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;owncloud&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;owncloud&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;80:80&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres:owncloud-db&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;postgres&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;postgres&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;POSTGRES_PASSWORD=mysecretpassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All you have to do now is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; to start all the containers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/docker-owncloud-running.png&quot; alt=&quot;Screenshot of ownCloud configured and running&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;persist-the-data&quot;&gt;Persist the data&lt;/h3&gt;

&lt;p&gt;Let’s add some persistence by adding some &lt;em&gt;data volumes&lt;/em&gt;: we want to create “data” containers for
PostgreSQL (for the data itself) and ownCloud (configurations and files stored).&lt;/p&gt;

&lt;p&gt;The folders we need to persist are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/postgresql&lt;/code&gt; for the database configuration&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/postgresql&lt;/code&gt; for the database content itself&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/html/data&lt;/code&gt; for owncloud’s data&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/html/config&lt;/code&gt; for owncloud’s configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is achieved using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file I uploaded &lt;a href=&quot;https://gist.github.com/MickaelBergem/524f8fcb39a3ad565663&quot;&gt;in this Gist&lt;/a&gt;, take a look at it.&lt;/p&gt;

&lt;p&gt;To check all the data is persisted, you can star files (or remove/create files), and then remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owncloud&lt;/code&gt; containers to check if the next &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; does persist these modifications:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; test_owncloud_1 test_postgres_1 &lt;span class=&quot;c&quot;&gt;# Replace &quot;test&quot; by the name of your folder&lt;/span&gt;
docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then go back in your browser:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/docker-owncloud-starred.png&quot; alt=&quot;The changes are still here!&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here you are! You have a fully functional ownCloud installation running locally!&lt;/p&gt;

&lt;h3 id=&quot;upgrade&quot;&gt;Upgrade?&lt;/h3&gt;

&lt;p&gt;Please note that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;owncloud-data_1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres-data_1&lt;/code&gt; containers should never be deleted.
To upgrade the other non-data containers (postgres or owncloud) for example when a new image is available:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker pull owncloud
docker pull postgres
docker-compose -d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; flag is to &lt;em&gt;detach&lt;/em&gt; from the containers so they can start in the background :)&lt;/p&gt;

&lt;h2 id=&quot;last-settings&quot;&gt;Last settings&lt;/h2&gt;

&lt;p&gt;To setup your own cloud, don’t forget to change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domainname&lt;/code&gt; lines in the compose file.&lt;/p&gt;

&lt;p&gt;You may also want to enable HTTPS, this will be for another blog post.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/serverside/2015/08/25/setting-up-owncloud-server-in-a-docker-container/</link>
                <guid>https://blog.securem.eu/serverside/2015/08/25/setting-up-owncloud-server-in-a-docker-container</guid>
                <pubDate>Tue, 25 Aug 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Tracking events with Piwik</title>
                <description>
&lt;h3 id=&quot;piwik-&quot;&gt;Piwik ?&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/pw_logo.png&quot; alt=&quot;Piwik logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Piwik is &lt;a href=&quot;http://piwik.org/what-is-piwik/&quot;&gt;the leading open source web analytics platform&lt;/a&gt;, and offers many advantages compared to Google Analytics (that I have used for years before switching to Piwik):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;free and open-source software&lt;/em&gt;: although Google Analytics is free too, Piwik is open-source and will always remain free and hackable&lt;/li&gt;
  &lt;li&gt;you can &lt;em&gt;install it on your own server&lt;/em&gt;: no data is sent to a third party and &lt;em&gt;you stay the master of the data&lt;/em&gt;, this is especially important for people (like me) thinking it is important not to share all your (and your customers’) data with Google or advertising companies&lt;/li&gt;
  &lt;li&gt;you have access to the data: you can use APIs, custom plugins or even manually execute SQL queries to build custom reports or bind a CRM to it&lt;/li&gt;
  &lt;li&gt;the &lt;em&gt;community&lt;/em&gt; is big, and the development is fast, as shown by the Github activity graphs :
&lt;a href=&quot;https://github.com/piwik/piwik/graphs/commit-activity&quot;&gt;&lt;img src=&quot;/assets/illustrations/pw_github_commits.png&quot; alt=&quot;Commits graph&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://github.com/piwik/piwik/graphs/contributors&quot;&gt;&lt;img src=&quot;/assets/illustrations/pw_github_stats.png&quot; alt=&quot;Contributions graph&quot; /&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Piwik appears to be lighter and to load faster (haven’t done the test myself)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main advantage I find in Piwik is &lt;strong&gt;you are in control&lt;/strong&gt;, if you need to remember one thing. Note that features may be more developed and advanced in Google Analytics than in Piwik, &lt;a href=&quot;http://www.zenincognito.com/piwik-vs-google-analytics-a-detailed-review/&quot;&gt;this blog post&lt;/a&gt; compares both.&lt;/p&gt;

&lt;h3 id=&quot;tracking-events&quot;&gt;Tracking events&lt;/h3&gt;

&lt;p&gt;Now, what ?&lt;/p&gt;

&lt;p&gt;Piwik and Google Analytics only track page views by default (who was where, when, how), but not what happened inside the page. And you may want to track more precisely the behavior of your visitors, for many different reasons :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;who clicked on which button, and triggered what action inside the page ?&lt;/li&gt;
  &lt;li&gt;who read the text to the end (footer) ?&lt;/li&gt;
  &lt;li&gt;which photos are the most hovered on &lt;a href=&quot;https://geoponts.enpc.fr/about&quot;&gt;the “about / team” page of your website like this one&lt;/a&gt; ? This one is a very nice idea for curious people like me (who is the most charismatic in your team ?)&lt;/li&gt;
  &lt;li&gt;when you have dynamic content : who visited tabs, accordions ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every time you need to track what happened on the client side, &lt;a href=&quot;http://piwik.org/docs/event-tracking/&quot;&gt;Piwik can track it&lt;/a&gt;, and this is really easy. All you have to do is to call the following piece of Javascript :&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;_paq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;trackEvent&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;CategoryName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ActionName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ActionValue&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example, in the case of the team pictures :&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;.team-member &amp;gt; img&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mouseover&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;_paq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;trackEvent&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Behavior&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;HoverPhoto&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is the result :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/pw_events.png&quot; alt=&quot;Event triggered Piwik&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that you can also pass an optional numeric value for the action :&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;_paq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;trackEvent&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Posts&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;RatingPost&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;PostName&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;8.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;tracking-events-directly-from-you-application-or-server&quot;&gt;Tracking events directly from you application or server&lt;/h3&gt;

&lt;p&gt;It is also possible, but I haven’t tried it yet, to trigger events from your backend, with &lt;a href=&quot;http://developer.piwik.org/api-reference/tracking-api&quot;&gt;the Tracking HTTP API&lt;/a&gt; : register when emails are sent, when an error occurs, or anytime something happens inside the backend !&lt;/p&gt;

&lt;p&gt;And if you want to join the community, &lt;a href=&quot;http://developer.piwik.org/&quot;&gt;head to the Piwik Developer Zone&lt;/a&gt; ! :)&lt;/p&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/assets/highlight.css&quot; /&gt;

&lt;script src=&quot;//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/highlight.min.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;hljs.initHighlightingOnLoad();&lt;/script&gt;

</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2015/03/09/tracking-events-with-piwik/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2015/03/09/tracking-events-with-piwik</guid>
                <pubDate>Mon, 09 Mar 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Flooding the Phisherman (with fake credentials)</title>
                <description>
&lt;p&gt;This morning, another phishing email in my inbox. Short message, and a link to &lt;a href=&quot;http://priveservicetemporaire.fr/&quot;&gt;http://priveservicetemporaire.fr/&lt;/a&gt; (targets French users).&lt;/p&gt;

&lt;p&gt;I remembered reading this tweet from &lt;a href=&quot;https://twitter.com/averagesecguy&quot;&gt;@averagesecguy&lt;/a&gt; :&lt;/p&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-cards=&quot;hidden&quot; lang=&quot;fr&quot;&gt;&lt;p&gt;Find a phishing site? Overwhelm it with fake credentials. &lt;a href=&quot;https://t.co/nS56XNb6ek&quot;&gt;https://t.co/nS56XNb6ek&lt;/a&gt; It&amp;#39;s preconfigured for a phishing site I found today.&lt;/p&gt;&amp;mdash; Stephen Haywood (@averagesecguy) &lt;a href=&quot;https://twitter.com/averagesecguy/status/569906030475399170&quot;&gt;23 Février 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async=&quot;&quot; src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;

&lt;p&gt;I didn’t found the tweet and the link in the first place, so I built &lt;a href=&quot;https://gist.github.com/MickaelBergem/811e6635f3054cad929a&quot;&gt;a small Python script&lt;/a&gt; to do the same. It just posts random credentials on the given URL and checks the response to be sure everything went fine.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/MickaelBergem/811e6635f3054cad929a.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;release-the-kraken-&quot;&gt;Release the Kraken !&lt;/h2&gt;

&lt;p&gt;Then I began to flood the phishing website :&lt;/p&gt;

&lt;pre&gt;&lt;small&gt;$ python3 phisherflooder.py
#0000   Envoi : (.abxlsniznaan@gmail.com, MHPTwHcJw)
#0001   Envoi : (ukyf.pksw8rnp_hxkimb2gena@laposte.net, RiwnRKQ6DVKiVOKM)
#0002   Envoi : (zc1fmuuzqrtsp8hxscpf@live.net, gThR4c)
#0003   Envoi : (rijnikhdj4pa4dtdjcmlc4nyvgwc@gmail.com, 1WP83dxul3O6ECx)
#0004   Envoi : (ezd9_92zbosrisy@orange.fr, AF8oKV3cEn)

[...]

#0075   Envoi : (l1x3e40vkjnua8zr@yahoo.com, nQWCCi)
&lt;/small&gt;&lt;/pre&gt;

&lt;p&gt;After the 75&lt;sup&gt;th&lt;/sup&gt; post, the flood stopped working: the website was throwing an error (“Error sending the message”).
My guess is that the form sends an email to the “phisherman” instead of storing the credentials on the server, and the hoster limits the number of sent email per hour (75 mail / hour ?). Trying a few hours after gave the same result.&lt;/p&gt;

&lt;p&gt;Now the phisherman is receiving hundreds of fake credentials into his mailbox, each day. Nice, eh ?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/phisherman.gif&quot; alt=&quot;Phisherman vs Water&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Okay, now even if someone fills in their credentials, nothing will be sent.
But even if I plan to let the script run until the website finally gets down, there is something more efficient to do: report the incident !&lt;/p&gt;

&lt;h2 id=&quot;reporting-phishing-incidents&quot;&gt;Reporting phishing incidents&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Whois&lt;/em&gt; can give us the “abuse” registrar contact, and information about who and when the domain name has been registered.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;whois priveservicetemporaire.fr -H
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sending an email to report the phishing website will hopefully result in the server being shut down.&lt;/p&gt;

&lt;p&gt;I then looked at the email, especially the headers to see from where did the email come from :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Received: from webmail.no-log.org (webmail.no-log.org [80.67.172.39])
    (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))
    (No client certificate requested)
    by lpn-prd-vrin014 (Postfix) with ESMTPS id 558E9180143;
    Sun,  8 Mar 2015 06:10:00 +0100 (CET)
Received: by webmail.no-log.org (Postfix, from userid 33)
    id 1802C4BDCC; Sun,  8 Mar 2015 06:10:00 +0100 (CET)
X-Squirrel-UserHash: QFEPDQEJKSAAAAAAABpdRlU=
X-Squirrel-FromHash: AgsCDl4DCio=
Message-ID: &amp;lt;cf9a229bb89f6718c704165a5462e8b3.squirrel@webmail.no-log.org&amp;gt;
Date: Sun, 8 Mar 2015 06:10:00 +0100
Subject: infos m.laposte
From: P.arametres-La-poste@no-log.org
User-Agent: SquirrelMail/1.4.21
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So this email has been sent from a webmail &lt;em&gt;webmail.no-log.org&lt;/em&gt; : their website shows an abuse email contact. I then sent them the full email (with headers) to them so they can block the corresponding account.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;received a phishing email&lt;/li&gt;
  &lt;li&gt;built a tool to flood the fake website with fake credentials&lt;/li&gt;
  &lt;li&gt;reached a limit that prevents new credentials to be submitted within a period of time&lt;/li&gt;
  &lt;li&gt;reported the phishing campaign to the hoster and the SMTP provider&lt;/li&gt;
&lt;/ul&gt;
</description>
                <link>https://blog.securem.eu/projects/2015/03/08/flooding-the-phisherman/</link>
                <guid>https://blog.securem.eu/projects/2015/03/08/flooding-the-phisherman</guid>
                <pubDate>Sun, 08 Mar 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Python ProTip : caching the PyPi packages downloaded by PIP</title>
                <description>
&lt;p&gt;&lt;strong&gt;WARNING: this method is deprecated since &lt;a href=&quot;https://pip.pypa.io/en/stable/news/&quot;&gt;pip 6.0&lt;/a&gt;, as caching is now enabled by default&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;the-how&quot;&gt;The How&lt;/h2&gt;

&lt;p&gt;Add the following lines in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.pip/pip.conf&lt;/code&gt; (which may not exist) :&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/MickaelBergem/9c32a9093a4009042d81.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;the-why&quot;&gt;The Why&lt;/h2&gt;

&lt;p&gt;A few weeks ago, I was still working (as an intern) at Theodo as an Agile Web Developer, deploying my source code several times a day on a staging server (and pushing to production at least once a week). This is &lt;strong&gt;continous deployment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The deployment script, written with &lt;a href=&quot;http://www.ansible.com/home&quot;&gt;Ansible&lt;/a&gt;, was creating a whole new build of the project in a new folder (then linked the “current” folder to this “release” build):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;downloading and building the assets for the frontend: Bootstrap, Font Awesome, etc. (with Bower) with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;installing the Python backend part by creating a new virtualenv inside the “release” folder, and then running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At each and every deployment, all the python packages needed were downloaded, then installed, which is time-consuming. Caching allow us to gain time on the downloading part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : To gain time on the installation part, it is also possible to use a common &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtualenv&lt;/code&gt; for all the releases, but there is the risk of suppressing a vital package in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt; file without noticing (only builds on brand new machines will fail). I also experienced a similar issue with the frontend part (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install&lt;/code&gt; downloads and install the JS packages in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./node_modules&lt;/code&gt;) where a package suddenly stopped working in the way we were using it : we noticed it immediately and fixed our code, instead of facing this bug during a production deployment.&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2015/02/06/python-protip-caching-the-pypi-packages-downloaded-by-pip/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2015/02/06/python-protip-caching-the-pypi-packages-downloaded-by-pip</guid>
                <pubDate>Fri, 06 Feb 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Unfollower : minimal Python script to track who unfollowed you</title>
                <description>
&lt;p&gt;You may have noticed that some Twitter accounts are massively following other people, adding several dozens of accounts each day, in order to be followed back and to increase their number of followers.
In the beginning, I followed some of them as they were matching some of my interest fields (entrepreneurship, web, new technologies…), but I then discovered that as soon as I followed them, some of them were unfollowing me.&lt;/p&gt;

&lt;p&gt;I guess this is to keep a “decent” number of “following” accounts (to not have 100x more accounts followed by you than following you).&lt;/p&gt;

&lt;p&gt;I also guess some of my followers either don’t find me interesting, or don’t like me posting French content (which I understand), but this is another question and it won’t be solved by a script ;)&lt;/p&gt;

&lt;p&gt;So I created &lt;a href=&quot;https://github.com/MickaelBergem/unfollower&quot;&gt;this Python script&lt;/a&gt;, which basically stores in a SQLite file (but you can configure any SQLAlchemy-compatible backend) the list of you follower and poll every hour the Twitter API to check if somebody unfollowed you.&lt;/p&gt;

&lt;p&gt;There are already many online tools which tracks unfollowers, with nice charts like &lt;a href=&quot;http://unfollowerstats.com/&quot;&gt;unfollowerstats.com&lt;/a&gt;, and even paid plans : &lt;a href=&quot;http://who.unfollowed.me/plans-pricing&quot;&gt;who.unfollowed.me&lt;/a&gt;. My script is not intended at all as a replacement of these web interfaces, but is a base to bind custom notifications or callbacks.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/MickaelBergem/unfollower&quot; class=&quot;bigbutton bigbutton-center&quot;&gt;Check the code !&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;technical-details&quot;&gt;Technical details&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;SQLAlchemy allows you to use a lot of database backend if you need / want to. By default, SQLite is used for its simplicity.&lt;/li&gt;
  &lt;li&gt;the Python script is ~60 lines and is fully customizable, whenever you want to adapt the notification system&lt;/li&gt;
  &lt;li&gt;using the Twitter API is really easy thanks to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-twitter&lt;/code&gt; package&lt;/li&gt;
  &lt;li&gt;in order to use the API, you will need &lt;a href=&quot;https://github.com/bear/python-twitter#api&quot;&gt;to create tokens and consumer key&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The installation is straightforward, &lt;a href=&quot;https://github.com/MickaelBergem/unfollower#installation&quot;&gt;just follow the instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I recommend using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python-twitter&lt;/code&gt; (instead of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twitter&lt;/code&gt; PyPi package), because I find it simpler and easier to use.&lt;/p&gt;

&lt;h3 id=&quot;demonstration&quot;&gt;Demonstration&lt;/h3&gt;

&lt;p&gt;At the first start, Unfollower will detect your followers and add them into the database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/unfollower-first.png&quot; alt=&quot;First launch&quot; title=&quot;First launch of Unfollower&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then, whenever someone unfollow you, a line will be printed :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/illustrations/unfollower-found.png&quot; alt=&quot;Unfollower detected&quot; title=&quot;Unfollower detected&quot; /&gt;&lt;/p&gt;
</description>
                <link>https://blog.securem.eu/projects/2015/02/05/unfollower-minimal-python-script-to-track-who-unfollowed-you/</link>
                <guid>https://blog.securem.eu/projects/2015/02/05/unfollower---minimal-python-script-to-track-who-unfollowed-you</guid>
                <pubDate>Thu, 05 Feb 2015 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Reloading this blog on GitHub Pages with Jekyll</title>
                <description>
&lt;p&gt;Hey there ! It has been a long time since I have posted anything on my old &lt;em&gt;Dotclear&lt;/em&gt; blog. I used to write things for myself, to share the little knowledge I gained, interesting websites, and good security practices.&lt;/p&gt;

&lt;p&gt;It was written in French, my mother tongue, and was left abandoned, but I felt that there would be a wide variety of topics I would love to talk about, and I wanted a new start. To be able to share with more people across the world, I also wanted to write the content in English. Sorry for the misspellings, mistakes, and other weird sentences, don’t hesitate to post a comment to correct me !&lt;/p&gt;

&lt;p&gt;I wanted a fast (my previous hoster, &lt;a href=&quot;http://www.olympe.in/&quot;&gt;Olympe Network&lt;/a&gt; , is doing a wonderful work to provide free, ad-free, and opensource hosting to ~50000 users, but the performances weren’t good enough), simple, and customizable blog.&lt;/p&gt;

&lt;h2 id=&quot;why-github-pages-and-jekyll&quot;&gt;Why Github Pages and Jekyll&lt;/h2&gt;

&lt;p&gt;First, &lt;em&gt;Github Pages&lt;/em&gt; is a really simple (free) way to publish static code : just push code in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh-pages&lt;/code&gt; branch of a repository and it will become available at http://yourusername.github.io/repositoryname/ ! GitHub pages are using &lt;a href=&quot;https://www.fastly.com/&quot;&gt;Fastly CDN&lt;/a&gt; to &lt;a href=&quot;https://github.com/blog/1715-faster-more-awesome-github-pages&quot;&gt;improve loading times&lt;/a&gt;, which makes it fast.&lt;/p&gt;

&lt;p&gt;But as only static (HTML for instance) pages can be committed, you will need &lt;a href=&quot;http://jekyllrb.com/&quot;&gt;&lt;em&gt;Jekyll&lt;/em&gt;&lt;/a&gt; to manage the global layout, structure and formatting :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Ability to write content &lt;em&gt;in markdown or textile&lt;/em&gt; in your favorite text-editor.&lt;/li&gt;
  &lt;li&gt;Ability to write and preview your content via localhost.&lt;/li&gt;
  &lt;li&gt;No internet connection required (to edit and preview the content).&lt;/li&gt;
  &lt;li&gt;Ability to publish via git.&lt;/li&gt;
  &lt;li&gt;Ability to host your blog on a static web-server, like GitHub Pages.&lt;/li&gt;
  &lt;li&gt;No database required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-&quot;&gt;How ?&lt;/h2&gt;

&lt;p&gt;I often do &lt;strong&gt;“bang-documentation”&lt;/strong&gt;, meaning that &lt;strong&gt;I only provide the key steps to achieve the goal&lt;/strong&gt;, assuming that you are able to find more detailed documentation by yourself if needed. If a part is unclear, though, please leave me a comment and I will fix it !&lt;/p&gt;

&lt;p&gt;I used &lt;a href=&quot;http://jekyllbootstrap.com/&quot;&gt;Jekyll Bootstrap&lt;/a&gt;, which use pre-built HTML Bootstrap pages instead of Liquid templating : directly editing HTML seems to me easier than learning a new templating language.&lt;/p&gt;

&lt;p&gt;Here is the process I followed to build this blog, you can find &lt;a href=&quot;http://jekyllbootstrap.com/usage/jekyll-quick-start.html&quot;&gt;the original quickstart guide here&lt;/a&gt; :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Fork &lt;a href=&quot;https://github.com/plusjade/jekyll-bootstrap.git&quot;&gt;https://github.com/plusjade/jekyll-bootstrap.git&lt;/a&gt; or clone/push it into a new public / private repository&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; git clone https://github.com/plusjade/jekyll-bootstrap.git jekyll-blog
 cd jekyll-blog
 git remote set-url origin git@github.com:USERNAME/USERNAME.github.com.git
 git push origin master:gh-pages
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add a CNAME file and entry in your DNS zone&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; echo &quot;blog.securem.eu&quot; &amp;gt; CNAME

 git add CNAME &amp;amp;&amp;amp; git ci -m &quot;Add CNAME file&quot; &amp;amp;&amp;amp; git push
 # You will also need to edit your DNS zone to point the right subdomain to your github pages (username.github.io)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install --user-install jekyll&lt;/code&gt; on your computer to install Jekyll&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/_config.yml&lt;/code&gt; to your needs (for example, the blog’s name or a Piwik / Google Analytics tracking ID)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Launch the built-in webserver with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll serve&lt;/code&gt; then head to &lt;a href=&quot;http://127.0.0.1:4000/&quot;&gt;http://127.0.0.1:4000/&lt;/a&gt; (will autoreload the server when you create/edit content, but it doesn’t watch the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/_config.yml&lt;/code&gt; file)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create your first blog post with&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; rake post title=&quot;My first blog post !&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Then edit the newly created file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/_posts/&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Choose a nice theme from &lt;a href=&quot;http://themes.jekyllbootstrap.com/&quot;&gt;http://themes.jekyllbootstrap.com/&lt;/a&gt; and install it with&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; rake theme:install git=&quot;https://github.com/jekyllbootstrap/theme-the-minimum.git&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Change the landing page in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/index.md&lt;/code&gt;. Pages can be placed anywhere (root directory or subdirectory to class them) but must contain the YAML Front-matter header.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Commit, push, and enjoy !&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

</description>
                <link>https://blog.securem.eu/news/2014/12/13/reloading-this-blog-on-github-pages-with-jekyll/</link>
                <guid>https://blog.securem.eu/news/2014/12/13/reloading-this-blog-on-github-pages-with-jekyll</guid>
                <pubDate>Sat, 13 Dec 2014 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>My old blog has been archived</title>
                <description>
&lt;p&gt;I was talking about my previous blog in &lt;a href=&quot;/news/2014/12/13/reloading-this-blog-on-github-pages-with-jekyll/&quot;&gt;my article about Jekyll and Github Pages&lt;/a&gt;, it has been archived today and will still be accessible at &lt;a href=&quot;http://archive-blog.securem.eu/&quot;&gt;archive-blog.securem.eu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://archive-blog.securem.eu/&quot;&gt;&lt;img src=&quot;/assets/illustrations/archived-blog.png&quot; alt=&quot;Access old blog&quot; title=&quot;Old SecureM blog&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will still be talking about web development, new technologies, and network security here, and in English now. I often use my blog as a reminder for complex manipulations or settings, but I also love to share things I may find or reviews of things I may try. Maybe the next post will be talking about performance of DNS Tunnels ! ;)&lt;/p&gt;

</description>
                <link>https://blog.securem.eu/news/2014/12/13/my-old-blog-has-been-archived/</link>
                <guid>https://blog.securem.eu/news/2014/12/13/my-old-blog-has-been-archived</guid>
                <pubDate>Sat, 13 Dec 2014 00:00:00 +0000</pubDate>
        </item>

        <item>
                <title>Jekyll Rake cheatsheet</title>
                <description>
&lt;p&gt;When you create blog posts or pages with Jekyll, here are the Rake commands to remember :&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Only the title parameter is mandatory
rake post title=&quot;Jekyll Rake cheatsheet&quot; tags=[&apos;Publishing&apos;, &apos;Markdown&apos;] category=&quot;Tips and tricks&quot; [date=&quot;2014-12-13&quot;]

rake page name=&quot;about.html&quot;
rake page name=&quot;subcategory/niceformatting/index.html&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
                <link>https://blog.securem.eu/tips%20and%20tricks/2014/12/13/jekyll-rake-cheatsheet/</link>
                <guid>https://blog.securem.eu/tips%20and%20tricks/2014/12/13/jekyll-rake-cheatsheet</guid>
                <pubDate>Sat, 13 Dec 2014 00:00:00 +0000</pubDate>
        </item>


</channel>
</rss>
