<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[TheLazyFox]]></title><description><![CDATA[A blog about tech focused on Docker, Synology and self-hosted solutions]]></description><link>https://blog.thelazyfox.xyz/</link><image><url>https://blog.thelazyfox.xyz/favicon.png</url><title>TheLazyFox</title><link>https://blog.thelazyfox.xyz/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Sun, 26 Apr 2026 17:04:08 GMT</lastBuildDate><atom:link href="https://blog.thelazyfox.xyz/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Geo-blocking with SWAG]]></title><description><![CDATA[SWAG can support MaxMind databases which are used to provide IP Geolocation and Online Fraud Prevention. It was initially tougher to setup but since end of 2021, it's now integrated through Linuxserver mods system. Let's dig into it!]]></description><link>https://blog.thelazyfox.xyz/geo-blocking-with-swag/</link><guid isPermaLink="false">62fa86db1e51e000012f25ed</guid><category><![CDATA[Security]]></category><category><![CDATA[Self-hosted]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Wed, 29 Jun 2022 15:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2022/06/publication-cover-geoblocking-swag.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2022/06/publication-cover-geoblocking-swag.png" alt="Geo-blocking with SWAG"><p>We talked about <a href="https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/">SWAG</a>, SWAG Dashboard and more recently <a href="https://blog.thelazyfox.xyz/block-malicious-connections-with-crowdsec-as-intrusion-prevention-system-on-top-of-swag/">how to integrate CrowdSec into SWAG</a>, let&apos;s talk about how to do some Geo-blocking with SWAG.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Setup SWAG to safely expose your self-hosted applications to the internet</div><div class="kg-bookmark-description">SWAG is a rebirth of letsencrypt docker image, a full fledged web server and reverse proxy that includes Nginx, Php7, Certbot (Let&#x2019;s Encrypt client) and Fail2ban.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="Geo-blocking with SWAG"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/publication-cover-swag.png" alt="Geo-blocking with SWAG"></div></a></figure><p>SWAG can support <a href="https://maxmind.com/?ref=blog.thelazyfox.xyz">MaxMind</a> databases which are used to provide IP Geolocation and Online Fraud Prevention. It was initially tougher to setup but since end of 2021, it&apos;s now integrated through <a href="https://mods.linuxserver.io/?ref=blog.thelazyfox.xyz">Linuxserver mods system</a>, so let me guide you into it!</p><h2 id="setup-the-mod">Setup the mod</h2><ol><li>First of all, you need to create an account on MaxMind to get a free license key, mandatory to get access to their databases. <a href="https://www.maxmind.com/en/geolite2/signup?ref=blog.thelazyfox.xyz">Sign up to their GeoLite2 service</a>, it&apos;s pretty straightforward</li><li>When you have the key, modify your SWAG <code>docker-compose.yml</code> file to add the MaxMind mod through an environment variable like this: <code>DOCKER_MODS=linuxserver/mods:swag-maxmind</code><br>If you already have mods, ensure they are separated by <code>|</code>, such as <code>DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-crowdsec|linuxserver/mods:swag-maxmind</code></li><li>Add another environment variable <code>MAXMINDDB_LICENSE_KEY=&lt;license-key&gt;</code> and include your license key into it</li><li>Restart your container with <code>docker compose up -d</code> to apply the changes</li><li>Configure nginx to load the maxmind configuration file by adding the line below in the <code>http</code> section of <code>/config/nginx/nginx.conf</code>.</li></ol><figure class="kg-card kg-code-card"><pre><code class="language-yaml">include /config/nginx/maxmind.conf;</code></pre><figcaption>Line to add into /config/nginx/nginx.conf http section</figcaption></figure><p>The setup is now ready!</p><h2 id="manage-whitelist-and-blocklist">Manage whitelist and blocklist</h2><p>You can manage your whitelist and blocklist per country by editing <code>/config/nginx/maxmind.conf</code> with your favorite text editor, you have to enter the ISO code of the country followed by <code>yes</code> or <code>no</code> .</p><p>It includes an example for blocking high risk countries from GilbN&apos;s list based on the Spamhaus statistics and Akamai&#x2019;s state of the internet report.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">map $geoip2_data_country_iso_code $geo-whitelist {
    default no;
    UK yes;
}

map $geoip2_data_country_iso_code $geo-blacklist {
    default yes; #If your country is listed below, remove it from the list
    CN no; #China
    RU no; #Russia
    HK no; #Hong Kong
    IN no; #India
    IR no; #Iran
    VN no; #Vietnam
    TR no; #Turkey
    EG no; #Egypt
    MX no; #Mexico
    JP no; #Japan
    KR no; #South Korea
    KP no; #North Korea
    PE no; #Peru
    BR no; #Brazil
    UA no; #Ukraine
    ID no; #Indonesia
    TH no; #Thailand
}</code></pre><figcaption>/config/nginx/maxmind.conf</figcaption></figure><h2 id="apply-to-your-definitions">Apply to your definitions</h2><p>To use your whitelist and blocklist, you need to include these few lines (line 9-10) in your SWAG definitions.</p><pre><code class="language-yaml"> server {
     listen 443 ssl;
     listen [::]:443 ssl;

     server_name some-app.*;
     include /config/nginx/ssl.conf;
     client_max_body_size 0;

     if ($lan-ip = yes) { set $geo-whitelist yes; }
     if ($geo-whitelist = no) { return 404; }

     location / {</code></pre><p>And that&apos;s it &#x1F680;! Restart the container to apply your configuration changes and any traffic coming for one of these countries is going to be automatically blocked!</p><p><em>PS: If you are using services like Cloudflare in front of your websites, it might not work as expected as your websites will be accessed by Cloudflare&apos;s IP addresses.</em></p><p></p>]]></content:encoded></item><item><title><![CDATA[Block malicious connections with CrowdSec as Intrusion Prevention System on top of SWAG]]></title><description><![CDATA[CrowdSec is a collaborative Intrusion Prevention System which make it overly powerful compared to Fail2Ban and it also provides the capability to share your setup across multiple hosts!]]></description><link>https://blog.thelazyfox.xyz/block-malicious-connections-with-crowdsec-as-intrusion-prevention-system-on-top-of-swag/</link><guid isPermaLink="false">62fa86db1e51e000012f25eb</guid><category><![CDATA[Docker]]></category><category><![CDATA[Security]]></category><category><![CDATA[Self-hosted]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Wed, 25 May 2022 15:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2022/05/publication-cover-crowdsec-swag-dashboard.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2022/05/publication-cover-crowdsec-swag-dashboard.png" alt="Block malicious connections with CrowdSec as Intrusion Prevention System on top of SWAG"><p>I recently talked to you about SWAG &amp; SWAG Dashboard, this time I&apos;m going to introduce to you the new Docker mod released by LSIO: <a href="https://github.com/linuxserver/docker-mods/tree/swag-crowdsec?ref=blog.thelazyfox.xyz">swag-crowdsec</a>!</p><p>This mod adds the <a href="https://crowdsec.net/?ref=blog.thelazyfox.xyz">CrowdSec</a> <a href="https://github.com/crowdsecurity/cs-nginx-bouncer/?ref=blog.thelazyfox.xyz">nginx bouncer</a> to SWAG, to be installed/updated during container start. <strong>It eases the usage of CrowdSec with SWAG so let&apos;s see how to install CrowdSec and benefit from this Docker mod.</strong></p><h2 id="what-is-crowdsec">What is CrowdSec?</h2><blockquote><a href="https://crowdsec.net/?ref=blog.thelazyfox.xyz">CrowdSec</a> is a free, open-source and collaborative IPS; it&apos;s like Fail2Ban but you share your bans with all of the other users to try and pre-emptively block malicious hosts [...] <br>Unlike fail2ban, which uses a single service for detection and blocking of malicious traffic, CrowdSec is modular, allowing you to detect and block across multiple hosts and to easily integrate with different services. <br>The basic building blocks are the CrowdSec agent which parses your logs and detects malicious behaviour, one or more Bouncers which do the actual blocking, the Central API which is hosted by CrowdSec themselves and allows you to push and pull community blocks, and the Local API which acts as a central coordinator on your network for all the other parts.<br>Source: <a href="https://www.linuxserver.io/blog/blocking-malicious-connections-with-crowdsec-and-swag?ref=blog.thelazyfox.xyz">LSIO</a> &amp; <a href="https://crowdsec.net/?ref=blog.thelazyfox.xyz">CrowdSec</a></blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-crowdsec-ecosystem.jpg" class="kg-image" alt="Block malicious connections with CrowdSec as Intrusion Prevention System on top of SWAG" loading="lazy" width="941" height="478" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/05/swag-crowdsec-ecosystem.jpg 600w, https://blog.thelazyfox.xyz/content/images/2022/05/swag-crowdsec-ecosystem.jpg 941w" sizes="(min-width: 720px) 720px"><figcaption>CrowdSec Ecosystem. Source: CrowdSec</figcaption></figure><p><strong>The main takeovers are that CrowdSec is a collaborative Intrusion Prevention System which make it overly powerful compared to Fail2Ban and it also provides the capability to share your setup across multiple hosts!</strong></p><h2 id="crowdsec-installation">CrowdSec installation</h2><p>First of all, we need to create required folders and add CrowdSec in our SWAG stack.</p><h3 id="create-the-configuration-for-crowdsec">Create the configuration for CrowdSec</h3><ol><li>Create the required folders into your SWAG stack&apos;s folder with <code>mkdir -p crowdsec/{crowdsec-db,crowdsec-config,dashboard}</code></li><li>Create the acquisition file for CrowdSec with <code>nano crowdsec/acquis.yaml</code></li><li>Fill the file with the example acquisition below from <a href="https://hub.crowdsec.net/author/crowdsecurity/collections/nginx?ref=blog.thelazyfox.xyz">CrowdSec collection for nginx</a></li><li>Save with CTRL + X </li></ol><figure class="kg-card kg-code-card"><pre><code class="language-yaml">filenames:
  - /var/log/nginx/*.log
labels:
  type: nginx</code></pre><figcaption>crowdsec/acquis.yaml</figcaption></figure><p>How it looks like on my server:</p><pre><code>~/docker/swag
&#x251C;&#x2500;&#x2500; config
&#x2502;   &#x251C;&#x2500;&#x2500; crontabs
&#x2502;   &#x251C;&#x2500;&#x2500; crowdsec
&#x2502;   &#x251C;&#x2500;&#x2500; custom-cont-init.d
&#x2502;   &#x251C;&#x2500;&#x2500; custom-services.d
&#x2502;   &#x251C;&#x2500;&#x2500; dns-conf
&#x2502;   &#x251C;&#x2500;&#x2500; etc
&#x2502;   &#x251C;&#x2500;&#x2500; fail2ban
&#x2502;   &#x251C;&#x2500;&#x2500; geoip2db
&#x2502;   &#x251C;&#x2500;&#x2500; keys
&#x2502;   &#x251C;&#x2500;&#x2500; log
&#x2502;   &#x251C;&#x2500;&#x2500; nginx
&#x2502;   &#x251C;&#x2500;&#x2500; php
&#x2502;   &#x2514;&#x2500;&#x2500; www
&#x251C;&#x2500;&#x2500; crowdsec
&#x2502;   &#x251C;&#x2500;&#x2500; acquis.yaml
&#x2502;   &#x251C;&#x2500;&#x2500; crowdsec-config
&#x2502;   &#x251C;&#x2500;&#x2500; crowdsec-db
&#x2502;   &#x2514;&#x2500;&#x2500; dashboard
&#x2514;&#x2500;&#x2500; docker-compose.yml</code></pre><h3 id="add-crowdsec-into-your-swag-stack">Add CrowdSec into your SWAG stack</h3><p>Now, let&apos;s open the SWAG docker-compose.yml and add CrowdSec into it!</p><ol><li>Open your docker compose file with <code>nano docker-compose.yml</code></li><li>Add the new service CrowdSec as below</li><li>&#x2139;&#xFE0F; Don&apos;t forget to update the PGID to match the one used for SWAG</li><li>Adapt the volumes paths if needed</li><li>Save with CTRL + X </li><li>Launch the stack with <code>docker compose up -d</code> and wait for everything to be started</li></ol><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &quot;2.1&quot;
services:
  swag:
    # Keep here your current SWAG configuration

  crowdsec:
    container_name: crowdsec
    image: crowdsecurity/crowdsec:latest
    restart: unless-stopped
    depends_on:
      - swag
    networks:
      default:
        ipv4_address: 172.18.25.3
    environment:
      - COLLECTIONS=crowdsecurity/nginx
      - GID=1000
    volumes:
      - ./config/log/nginx:/var/log/nginx
      - ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
      - ./crowdsec/crowdsec-db:/var/lib/crowdsec/data/
      - ./crowdsec/crowdsec-config:/etc/crowdsec/
    security_opt:
      - no-new-privileges=true

networks:
  default:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br_swag
    ipam:
      config:
        - subnet: 172.18.25.0/24
          gateway: 172.18.25.255</code></pre><figcaption>docker-compose.yml for SWAG with CrowdSec</figcaption></figure><p>Now it&apos;s time to add a CrowdSec bouncer!</p><h3 id="what-is-a-bouncer">What is a bouncer?</h3><p>The CrowdSec bouncers are standalone software pieces in charge of acting upon a <u>decision</u> taken by CrowdSec like blocking an IP, presenting a captcha, enforcing MFA on a given user, etc.</p><p>For example, the <a href="https://github.com/crowdsecurity/cs-nginx-bouncer?ref=blog.thelazyfox.xyz" rel="noopener noreferrer">nginx bouncer</a> will check every unknown IP against the local API before letting go through or serving a <em>403</em> to the user, while a <a href="https://hub.crowdsec.net/author/crowdsecurity/bouncers/cs-firewall-bouncer?ref=blog.thelazyfox.xyz" rel="noopener noreferrer">firewall bouncer</a> or a <a href="https://hub.crowdsec.net/author/crowdsecurity/bouncers/cs-cloudflare-bouncer?ref=blog.thelazyfox.xyz" rel="noopener noreferrer">Cloudflare bouncer</a> will simply &quot;add&quot; malevolent IPs to nftables/ipset set of blacklisted IPs.</p><h3 id="create-a-bouncer-and-integrate-it-in-the-configuration">Create a bouncer and integrate it in the configuration</h3><p>You just need to run a single command to create your bouncer!</p><ol><li>Run <code>docker exec -t crowdsec cscli bouncers add bouncer-swag</code></li><li>It creates instantly the bouncer and print the related API key</li><li>&#x2139;&#xFE0F; Copy the API key you will need it soon</li><li>Open your docker compose file with <code>nano docker-compose.yml</code></li><li>Add the new environment variables DOCKER_MODS, CROWDSEC_API_KEY, CROWDSEC_LAPI_URL in your SWAG service (not crowdsec one!) as below.<br>&#x26A0;&#xFE0F; Be careful to change the IP address to target the <em>crowdsec</em> service&apos;s IP or replace the IP by the service name (crowdsec in my example).<br>&#x26A0;&#xFE0F;If you are using the SWAG Dashboard, you need to separate both mods with a pipe.</li><li>Save with CTRL + X </li><li>Launch the stack with <code>docker compose up -d</code> and wait for everything to be started</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-crowdsec-bouncer.jpg" class="kg-image" alt="Block malicious connections with CrowdSec as Intrusion Prevention System on top of SWAG" loading="lazy" width="580" height="78"><figcaption>Bouncer creation</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-yaml">      - DOCKER_MODS=linuxserver/mods:swag-crowdsec
      - CROWDSEC_API_KEY=916ae26c4d744fa862bcf9cdc29e30b7
      - CROWDSEC_LAPI_URL=http://172.18.25.3:8080</code></pre><figcaption>New environment variables for the SWAG service</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>      - DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-crowdsec
      - CROWDSEC_API_KEY=916ae26c4d744fa862bcf9cdc29e30b7
      - CROWDSEC_LAPI_URL=http://172.18.25.3:8080</code></pre><figcaption>New environment variables for the SWAG service if you are using SWAG Dashboard</figcaption></figure><p>Your docker-compose.yml file should look like this now:</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &quot;2.1&quot;
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    networks:
      default:
        ipv4_address: 172.18.25.2
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Zurich
      - URL=yourdomain.xyz
      - SUBDOMAINS=wildcard
      - VALIDATION=dns
      - DNSPLUGIN=cloudflare
      - EMAIL=youremailaddress@protonmail.com
      - DOCKER_MODS=linuxserver/mods:swag-dashboard|linuxserver/mods:swag-crowdsec
      - CROWDSEC_API_KEY=916ae26c4d744fa862bcf9cdc29e30b7
      - CROWDSEC_LAPI_URL=http://172.18.25.3:8080
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80

  crowdsec:
    container_name: crowdsec
    image: crowdsecurity/crowdsec:latest
    restart: unless-stopped
    depends_on:
      - swag
    networks:
      default:
        ipv4_address: 172.18.25.3
    environment:
      - COLLECTIONS=crowdsecurity/nginx
      - GID=1000
    volumes:
      - ./config/log/nginx:/var/log/nginx
      - ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
      - ./crowdsec/crowdsec-db:/var/lib/crowdsec/data/
      - ./crowdsec/crowdsec-config:/etc/crowdsec/
    security_opt:
      - no-new-privileges=true

networks:
  default:
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br_swag
    ipam:
      config:
        - subnet: 172.18.25.0/24
          gateway: 172.18.25.255</code></pre><figcaption>Complete docker-compose.yml with SWAG, SWAG Dashboard and CrowdSec</figcaption></figure><p>&#x1F44F; CrowdSec is setup! You are now protected by a community driven Intrusion Prevention System!</p><p>Few useful commands:</p><ul><li><code>docker exec -t crowdsec cscli metrics</code><br>Fetch metrics from the prometheus server and display them in a human-friendly way</li><li><code>docker exec -t crowdsec cscli decisions list</code><br>List decisions from LAPI</li><li><code>docker exec -t crowdsec cscli decisions delete --all</code><br>Delete List decisions from LAPI</li><li><code>docker exec -t crowdsec cscli alerts flush</code><br>Flush alerts -&#x26A0;&#xFE0F; This command can be used only on the same machine than the local API</li></ul><p>If you need more commands, please refer to the <a href="https://doc.crowdsec.net/docs/cscli/cscli?ref=blog.thelazyfox.xyz">official documentation</a>.</p><h2 id="create-a-crowdsec-dashboard">Create a CrowdSec dashboard </h2><p>We like dashboards, yes we &#x2764;&#xFE0F; dashboards so let&apos;s create one for CrowdSec with Metabase!</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &quot;2.1&quot;
services:
  swag:
    # Keep here your current SWAG configuration

  crowdsec:
    # Keep here your current CrowdSec configuration
     
  dashboard:
    container_name: crowdsec-dashboard
    build: ./crowdsec/dashboard
    restart: unless-stopped
    depends_on:
      - crowdsec
    networks:
      default:
        ipv4_address: 172.18.25.4
    ports:
      - 1111:3000
    environment:
      - MB_DB_FILE=/data/metabase.db
      - MGID=1000
    volumes:
      - ./crowdsec/crowdsec-db:/metabase-data/</code></pre><figcaption>docker-compose.yml for SWAG + CrowdSec with a Metabase dashboard</figcaption></figure><ol><li>Go in the dashboard folder created before (I knew you like dashboards!) with <code>cd crowdsec/dashboard</code></li><li>Get the Dockerfile provided by LSIO with <code>wget https://raw.githubusercontent.com/crowdsecurity/example-docker-compose/main/crowdsec/dashboard/Dockerfile</code></li><li>Add the dashboard service in your <code>docker-compose.yml</code> file like above</li><li>Save with CTRL + X</li><li>Launch the stack with <code>docker compose up -d</code> and wait for everything to be started</li><li>Access your dashboard on <code>https://your-host-ip:1111</code></li><li>Default&apos;s credentials are <code>crowdsec@crowdsec.net</code> and <code>!!Cr0wdS3c_M3t4b4s3??</code> </li></ol><p>&#x1F389; Congrats! You can now browse the dashboards prepared for you and even create your owns!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-crowdsec-dashboard.jpg" class="kg-image" alt="Block malicious connections with CrowdSec as Intrusion Prevention System on top of SWAG" loading="lazy" width="1900" height="954" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/05/swag-crowdsec-dashboard.jpg 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2022/05/swag-crowdsec-dashboard.jpg 1000w, https://blog.thelazyfox.xyz/content/images/size/w1600/2022/05/swag-crowdsec-dashboard.jpg 1600w, https://blog.thelazyfox.xyz/content/images/2022/05/swag-crowdsec-dashboard.jpg 1900w" sizes="(min-width: 720px) 720px"><figcaption>CrowdSec&apos;s information through a Metabase dashboard</figcaption></figure><h2 id="link-your-instance-to-crowdsec-console">Link your instance to CrowdSec Console</h2><p>You can also decide to link your CrowdSec instance to the CrowdSec Cloud Console to monitor alerts and manage several instances.</p><ol><li>Create an account on <a href="https://app.crowdsec.net/?ref=blog.thelazyfox.xyz">https://app.crowdsec.net</a></li><li>After login, you will see a command to enroll your instance, execute it through Docker with <code>docker exec -it crowdsec cscli console enroll thisisyourtoken</code></li><li>Go back to the website to accept the enrollment</li><li>Restart CrowdSec with <code>docker restart crowdsec</code></li></ol><p>You can now monitor your instance from the CrowdSec Console!</p><p>Ressources to go deeper:</p><ul><li><a href="https://www.linuxserver.io/blog/blocking-malicious-connections-with-crowdsec-and-swag?ref=blog.thelazyfox.xyz">https://www.linuxserver.io/blog/blocking-malicious-connections-with-crowdsec-and-swag</a></li><li><a href="https://github.com/crowdsecurity?ref=blog.thelazyfox.xyz">https://github.com/crowdsecurity</a></li><li><a href="https://github.com/crowdsecurity/cs-nginx-bouncer/?ref=blog.thelazyfox.xyz">https://github.com/crowdsecurity/cs-nginx-bouncer/</a></li><li><a href="https://github.com/linuxserver/docker-mods/tree/swag-crowdsec?ref=blog.thelazyfox.xyz">https://github.com/linuxserver/docker-mods/tree/swag-crowdsec</a></li><li><a href="https://crowdsec.net/?ref=blog.thelazyfox.xyz">https://crowdsec.net/</a></li><li><a href="https://doc.crowdsec.net/docs/cscli/cscli?ref=blog.thelazyfox.xyz">https://doc.crowdsec.net/docs/cscli/cscli</a></li><li><a href="https://hub.crowdsec.net/author/crowdsecurity/collections/nginx?ref=blog.thelazyfox.xyz">https://hub.crowdsec.net/author/crowdsecurity/collections/nginx</a></li><li><a href="https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/">https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/</a></li></ul><p>Big Up EVO for support! &#x2665;</p>]]></content:encoded></item><item><title><![CDATA[Get the most out of your SWAG with a dashboard!]]></title><description><![CDATA[The LSIO team has released sometimes ago a dashboard to monitor what is happening on your SWAG, here is how to implement it!]]></description><link>https://blog.thelazyfox.xyz/get-the-most-out-of-your-swag-with-a-dashboard/</link><guid isPermaLink="false">62fa86db1e51e000012f25ec</guid><category><![CDATA[Docker]]></category><category><![CDATA[Security]]></category><category><![CDATA[Self-hosted]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Fri, 20 May 2022 14:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2022/05/publication-cover-swag-dashboard.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2022/05/publication-cover-swag-dashboard.png" alt="Get the most out of your SWAG with a dashboard!"><p>Few months ago, I&apos;ve introduced to you SWAG! &#xA0;</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Setup SWAG to safely expose your self-hosted applications to the internet</div><div class="kg-bookmark-description">SWAG is a rebirth of letsencrypt docker image, a full fledged web server and reverse proxy that includes Nginx, Php7, Certbot (Let&#x2019;s Encrypt client) and Fail2ban.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="Get the most out of your SWAG with a dashboard!"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/publication-cover-swag.png" alt="Get the most out of your SWAG with a dashboard!"></div></a></figure><p>The LSIO team has released sometimes ago a dashboard to monitor what is happening on your SWAG, here is how to implement it!</p><blockquote><a href="https://github.com/linuxserver/docker-mods/tree/swag-dashboard?ref=blog.thelazyfox.xyz">SWAG Dashboard</a> is a mod powered by <a href="https://goaccess.io/?ref=blog.thelazyfox.xyz">GoAccess</a> that provides a comprehensive overview of SWAG&apos;s operation.</blockquote><p>This dashboard is provided by LSIO through a powerful <strong>Docker mod</strong> so it&apos;s super easily to setup!<br><br>You just need to integrate this line in your environment variables:</p><pre><code class="language-yaml">- DOCKER_MODS=linuxserver/mods:swag-dashboard</code></pre><p>Let&apos;s add it into the full SWAG docker compose example from the article above, it will look like this:</p><pre><code>version: &quot;2.1&quot;
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Zurich
      - URL=yourdomain.xyz
      - SUBDOMAINS=wildcard
      - VALIDATION=dns
      - DNSPLUGIN=cloudflare
      - EMAIL=youremailaddress@protonmail.com
      - DOCKER_MODS=linuxserver/mods:swag-dashboard
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80
    labels:
      - com.centurylinklabs.watchtower.enable=true
      - deunhealth.restart.on.unhealthy=true</code></pre><p>For security reasons, the dashboard can only be accessible through the URL <strong>dashboard.yourdomain.xyz </strong>but obviously it&apos;s not something we want to open to the world. To access it, you need to create a DNS rewrite.</p><p>On my side, I&apos;m using AdGuard Home which grants the ability to do DNS rewriting for all devices on my network, it suits perfectly that need. </p><p>You can also use other solutions, feel free to share them with to complete this article&#x1F609;.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/adguard-home-a-world-without-ads-and-trackers/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">AdGuard Home: A world without ads and trackers!</div><div class="kg-bookmark-description">You might have heard about Ublock Origin or PiHole, let me introduce to you AdGuard, and especially AdGuard Home!</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="Get the most out of your SWAG with a dashboard!"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-adguard.png" alt="Get the most out of your SWAG with a dashboard!"></div></a></figure><h3 id="dns-rewrite-with-adguard-home">DNS rewrite with Adguard Home</h3><p>If you are an Adguard Home user:</p><ol><li>Open AdGuard</li><li>Go to Filters &gt; DNS rewrites</li><li>Click on Add DNS rewrite</li><li>As domain name, enter <strong>dashboard.yourdomain.xyz</strong> (replace with your own domain)</li><li>As IP address, enter the IP address of your host where SWAG is running</li><li>Click on Save</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-adguard-rewrite.jpg" class="kg-image" alt="Get the most out of your SWAG with a dashboard!" loading="lazy" width="1277" height="619" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/05/swag-dashboard-adguard-rewrite.jpg 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2022/05/swag-dashboard-adguard-rewrite.jpg 1000w, https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-adguard-rewrite.jpg 1277w" sizes="(min-width: 720px) 720px"><figcaption>DNS rewrites in AdGuard</figcaption></figure><h3 id="dns-rewrite-with-pihole">DNS rewrite with PiHole</h3><p>If you are a PiHole user:</p><ol><li>Open PiHole</li><li>Go to Local DNS Records</li><li>As domain name, enter <strong>dashboard.yourdomain.xyz</strong> (replace with your own domain)</li><li>As IP address, enter the IP address of your host where SWAG is running</li><li>Click on Add</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-pihole-rewrite.jpg" class="kg-image" alt="Get the most out of your SWAG with a dashboard!" loading="lazy" width="1009" height="259" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/05/swag-dashboard-pihole-rewrite.jpg 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2022/05/swag-dashboard-pihole-rewrite.jpg 1000w, https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-pihole-rewrite.jpg 1009w" sizes="(min-width: 720px) 720px"><figcaption>DNS rewrites in PiHole</figcaption></figure><p>Here we go! &#x1F680;</p><p>You can now enjoy and dig into the good looking dashboard by accessing <strong>dashboard.yourdomain.xyz</strong> (replace with your own domain) &#x1F389;</p><p>If you are looking for more details, please visit the <a href="https://www.linuxserver.io/blog/introducing-swag-dashboard?ref=blog.thelazyfox.xyz">official blog post from LSIO</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-example.png" class="kg-image" alt="Get the most out of your SWAG with a dashboard!" loading="lazy" width="1666" height="1207" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/05/swag-dashboard-example.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2022/05/swag-dashboard-example.png 1000w, https://blog.thelazyfox.xyz/content/images/size/w1600/2022/05/swag-dashboard-example.png 1600w, https://blog.thelazyfox.xyz/content/images/2022/05/swag-dashboard-example.png 1666w" sizes="(min-width: 720px) 720px"><figcaption>SWAG Dashboard</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Setup SWAG to safely expose your self-hosted applications to the internet]]></title><description><![CDATA[SWAG is a rebirth of letsencrypt docker image, a full fledged web server and reverse proxy that includes Nginx, Php7, Certbot (Let's Encrypt client) and Fail2ban. ]]></description><link>https://blog.thelazyfox.xyz/setup-swag-to-safely-expose-your-self-hosted-applications-to-the-internet/</link><guid isPermaLink="false">62fa86db1e51e000012f25ea</guid><category><![CDATA[Self-hosted]]></category><category><![CDATA[Docker]]></category><category><![CDATA[SSL]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 31 Jan 2022 05:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2022/01/publication-cover-swag.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2022/01/publication-cover-swag.png" alt="Setup SWAG to safely expose your self-hosted applications to the internet"><p>Few years ago, I was using the reverse proxy provided by my Synology NAS and generating my SSL certificate directly with the <a href="https://fleet.linuxserver.io/image?name=linuxserver%2Fletsencrypt&amp;ref=blog.thelazyfox.xyz">LetsEncrypt container by LinuxServer</a> which is now depreciated. &#xA0;</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/generate-a-lets-encrypt-wildcard-certificate-on-synology-with-docker-and-cloudflare/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Generate a Let&#x2019;s Encrypt wildcard certificate on Synology with Docker and Cloudflare</div><div class="kg-bookmark-description">Following my setup of AdGuard Home, I found out it can manage DNS-over-HTTPS and DNS-over-TLS but it needs valid SSL certificates for that purpose.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="Setup SWAG to safely expose your self-hosted applications to the internet"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-le-synology-cloudflare.png" alt="Setup SWAG to safely expose your self-hosted applications to the internet"></div></a></figure><p>For few months now, I&apos;m running with SWAG!</p><figure class="kg-card kg-image-card"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/swag-1-.gif" class="kg-image" alt="Setup SWAG to safely expose your self-hosted applications to the internet" loading="lazy" width="200" height="150"></figure><h2 id="what-is-swag">What is SWAG?</h2><blockquote>Swag is a term that refers to a person&apos;s sense of style or skills. It is derived from &quot;swagger&quot; and was made popular by hip-hop culture.</blockquote><p>Seriously speaking, <a href="https://hub.docker.com/r/linuxserver/swag?ref=blog.thelazyfox.xyz">SWAG</a> is a rebirth of <a href="https://hub.docker.com/r/linuxserver/letsencrypt?ref=blog.thelazyfox.xyz">letsencrypt docker image</a>, a full fledged web server and reverse proxy that includes Nginx, Php7, Certbot (Let&apos;s Encrypt client) and Fail2ban. No more confusion or trademark issue related to the name &quot;letsencrypt&quot;.</p><blockquote>SWAG is really a LEMP stack minus the M. For those unfamiliar, the letters stand for L=Linux, E=Nginx (because it&apos;s pronounced Engine-X), M=MySQL/MariaDB and P=PHP. SWAG has all but MySQL/MariaDB, for which we recommend pairing with our <a href="https://hub.docker.com/r/linuxserver/mariadb?ref=blog.thelazyfox.xyz">MariaDB docker image</a> if needed. Apart from those, SWAG has the Let&apos;s Encrypt client Certbot integrated, for automating retrieval and management of free SSL certs. It also has Fail2ban for intrusion detection and prevention.</blockquote><h2 id="how-to-setup-swag-for-your-domain">How to setup SWAG for your domain?</h2><h3 id="docker-compose">Docker Compose</h3><p>First thing first, you need to create a folder for this project and put inside your <em>docker-compose.yml</em> file.</p><pre><code class="language-yaml">version: &quot;2.1&quot;
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Zurich
      - URL=yourdomain.xyz
      - SUBDOMAINS=wildcard
      - VALIDATION=dns
      - DNSPLUGIN=cloudflare
      - EMAIL=youremailaddress@protonmail.com
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80
    labels:
      - com.centurylinklabs.watchtower.enable=true
      - deunhealth.restart.on.unhealthy=true</code></pre><p><em>Please remember to create all folders and files before trying to launch your docker-compose.</em></p><p>How does it look like on my server:</p><pre><code>swag/
      |_ docker-compose.yaml
      |_ config/</code></pre><p>Before starting, let&apos;s <s>encrypt</s> speak about the content of this docker compose file.</p><ul><li><strong>URL</strong><br>This is the URL of your domain. It will be used to generate the SSL certificate but also for the DNS validation</li><li><strong>SUBDOMAINS</strong><br>You can list all subdomains you would like to cover with your SSL certificate or just set <code>wildcard</code> to cover them all. It will result with a certification for *.yourdomain.xyz</li><li><strong>VALIDATION</strong><br>This describe the method used by the container to validate your ownership of the above-mentionned domain. We are going to use the DNS validation.</li><li><strong>DNSPLUGIN</strong><br>I&apos;m setting <code>cloudflare</code> which is my DNS as you will see below. It&apos;s one of the various DNS supported by SWAG. <em>More information on the <a href="https://docs.linuxserver.io/general/swag?ref=blog.thelazyfox.xyz#create-container-via-dns-validation-with-a-wildcard-cert">official documentation</a></em></li><li><strong>EMAIL</strong><br>Your email address to be used to get your free Let&apos;s Encrypt SSL certificate for your domain</li></ul><h3 id="dns-provider">DNS provider</h3><p><a href="https://www.cloudflare.com/?ref=blog.thelazyfox.xyz">Cloudflare</a> is recommended due to being free and reliable. To switch to Cloudflare, you can register for a free account and follow their steps to point the nameservers to Cloudflare. The rest of the instructions assume that you are using the cloudflare DNS plugin but it&apos;s quite the same for others.</p><p>On your DNS provider, you will need to create an A record for the main domain and point it to our server IP. Then you will also create a CNAME for all your subdomains or a CNAME for * and point it to the A record for the domain. On Cloudflare, we&apos;ll click on the orange cloud to turn it grey so that it is DNS only and not cached/proxied by Cloudflare, which would add more complexities.</p><p>You can now start your docker container for the first time to generate all the files in your config folder with <code>docker-compose up</code>. You might see some errors because we didn&apos;t provided all information yet for the DNS validation but you need that first start to generate the required files. Exit with <code>CTRL + C</code>.</p><p>You can now edit the file <code>/swag/config/dns-conf/cloudflare.ini</code>with your favorite text editor and add your Cloudflare email address and <a href="https://dash.cloudflare.com/profile/api-tokens?ref=blog.thelazyfox.xyz">Global API key</a>. Save it and relaunch the container with <code>docker-compose up -d</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-ini">dns_cloudflare_email = youremailaddress@protonmail.com
dns_cloudflare_api_key = yourglobalapikey</code></pre><figcaption>/swag/config/dns-conf/cloudflare.ini</figcaption></figure><p><strong>&#x1F389; Congrats! </strong><br><strong>SWAG is running and your Let&apos;s Encrypt SSL certificate has been generated for your domain!</strong></p><p>SWAG is running on your server, you can see it by accessing it on port 443 (HTTPS) or 80 (HTTP) of your local machine.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/image.png" class="kg-image" alt="Setup SWAG to safely expose your self-hosted applications to the internet" loading="lazy" width="950" height="338" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/01/image.png 600w, https://blog.thelazyfox.xyz/content/images/2022/01/image.png 950w" sizes="(min-width: 720px) 720px"><figcaption>SWAG landing page</figcaption></figure><h3 id="reverse-proxy">Reverse Proxy</h3><blockquote>A reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers. These resources are then returned to the client as if they originated from the Web server itself.</blockquote><p>You need now to setup a reverse proxy to redirect each of your subdomains to the correct self-hosted application. No worries, SWAG comes with Nginx embedded!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/reverseproxy-1-.png" class="kg-image" alt="Setup SWAG to safely expose your self-hosted applications to the internet" loading="lazy" width="1612" height="710" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2022/01/reverseproxy-1-.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2022/01/reverseproxy-1-.png 1000w, https://blog.thelazyfox.xyz/content/images/size/w1600/2022/01/reverseproxy-1-.png 1600w, https://blog.thelazyfox.xyz/content/images/2022/01/reverseproxy-1-.png 1612w" sizes="(min-width: 720px) 720px"><figcaption>Reverse proxy schema. Source: Linuxserver</figcaption></figure><p>The SWAG image comes with a list of preset reverse proxy configuration files for popular apps and services such as Jellyfin, Heimdall, Vaultwarden, etc. They are all <a href="https://github.com/linuxserver/reverse-proxy-confs?ref=blog.thelazyfox.xyz">hosted on GitHub</a> and are automatically pulled into the <code>/swag/config/nginx/proxy-confs</code> folder as inactive sample files. </p><p>Let&apos;s active some of them to get access to our applications from Internet! <br>To activate one, you must rename the conf file to remove <code>.sample</code> from its filename and restart the SWAG container. </p><p>You can simply do <code>cp jellyfin.subdomain.conf.sample jellyfin.subdomain.conf</code>to get your configuration file ready for editing for the application Jellyfin.</p><p>Now let&apos;s see what is inside the file with <code>nano jellyfin.subdomain.conf</code>.<br>What matter for us is pretty simple:</p><ul><li><strong>server_name jellyfin.*; </strong><br>Only destination addresses that match <code>jellyfin.*</code> will match this server block. You need to replace <code>jellyfin</code>by the subdomain name you want to use</li><li><strong>set $upstream_app jellyfin;</strong><br>This is the container name used as dns hostname of the application your want to target. You can also replace it by the IP of the machine (e.g. 192.128.1.152)<br>If you use the hostname, please ensure SWAG and Jellyfin share the same network</li><li><strong>set $upstream_port 8096;</strong><br>This is the port where the application is running. You can replace it by your Jellyfin port. If above, you used the IP of the machine, use the external port otherwise the internal one </li><li><strong>set $upstream_proto http;</strong><br>This is the protocol</li><li><em>If you want to know more about the structure of this file, please refer to the official documentation about <a href="https://docs.linuxserver.io/general/swag?ref=blog.thelazyfox.xyz#understanding-the-proxy-conf-structure">understanding the proxy conf structure</a></em></li></ul><p>With this configuration, any hit on <code>jellyfin.yourdomain.xyz</code>will be redirected to your local application <code>http://192.168.1.152:8096</code>.</p><p>Don&apos;t forget to update the variable&apos;s values in all <code>location</code> section of the file</p><p><strong>&#x1F389; Congrats! </strong><br><strong>Your self-hosted application is now accessible from the Internet!</strong></p><h2 id="extra-security-layers">Extra security layers</h2><h3 id="enable-hsts">Enable HSTS</h3><blockquote><strong><a href="https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security?ref=blog.thelazyfox.xyz">HTTP Strict Transport Security</a></strong> (HSTS) is a simple and <a href="https://caniuse.com/?ref=blog.thelazyfox.xyz#feat=stricttransportsecurity">widely supported</a> standard to protect visitors by ensuring that their browsers <em>always</em> connect to a website over HTTPS. HSTS exists to remove the need for the common, insecure practice of redirecting users from <code>http://</code> to <code>https://</code> URLs. Source: <a href="https://https.cio.gov/hsts/?ref=blog.thelazyfox.xyz">https://https.cio.gov/hsts/</a>.</blockquote><p>It&apos;s very simple to activate with SWAG!</p><ol><li>Edit the file <code>ssl.conf</code> with <code>nano swag/config/nginx/ssl.conf</code></li><li>Uncomment the line <code>add_header Strict-Transport-Security &quot;max-age=63072000; includeSubDomains; preload&quot; always;</code></li><li>Save with CTRL + X </li><li>Restart SWAG with <code>docker restart swag</code></li></ol><h3 id="setup-fail2ban">Setup Fail2ban</h3><p>Fail2Ban is an intrusion prevention software framework that protects servers from brute-force attacks. It&apos;s also included in the SWAG docker image! Let&apos;s take your Jellyfin application as example.</p><p><strong>Create a new jail</strong></p><p>First, you need to edit the list of Fail2ban jails to add one for this application with <code>nano /swag/config/fail2ban/jail.local </code>and add a new section for Jellyfin application.</p><figure class="kg-card kg-code-card"><pre><code>[jellyfin]
enabled  = true
filter   = jellyfin
port     = http,https
logpath  = /config/log/nginx/access.log
maxretry = 4</code></pre><figcaption>/swag/config/fail2ban/jail.local</figcaption></figure><ul><li><strong>enabled</strong><br>Define if the jail is active or not</li><li><strong>filter</strong><br>The filter name used for this jail, we will create it after</li><li><strong>port</strong><br>Which standard ports it covers. It can include http, https, ssh, sftp and many others</li><li><strong>logpath</strong><br>Path to the log file which is provided to the filter</li><li><strong>maxretry</strong><br>Number of matches which triggers ban action on the IP</li><li>bantime<br>Duration (in seconds) for IP to be banned for. Negative number for &quot;permanent&quot; ban</li></ul><p><u>NOTE:</u> Each parameters can be overriden in each jail, if missing it will fallback to the one from the [DEFAULT] jail defined on top of <code>/swag/config/fail2ban/jail.local</code>.</p><p><strong>Create a new filter</strong></p><p>Secondly, you need to create a filter to be used on the logs above-mentionned to define what to catch and count as intrusion tentatives by using <code>nano /swag/config/fail2ban/filter.d/jellyfin.conf</code> and paste this filter inside to catch all 401 errors.</p><figure class="kg-card kg-code-card"><pre><code>[Definition]
failregex = ^&lt;HOST&gt; \- \S+ \[\] \&quot;(GET|POST|CONNECT|PUT|DELETE) \/.*? \S+\&quot; 401 .+$</code></pre><figcaption>/swag/config/fail2ban/filter.d/jellyfin.conf</figcaption></figure><p>You can now save the file and restart SWAG with <code>docker restart swag</code>.</p><p><strong>Test your Fail2Ban setup</strong></p><p>It&apos;s actually pretty easy to test!</p><ol><li>Connect yourself to a VPN server</li><li>Access your website via <code>https://jellyfin.yourdomain.xyz</code></li><li>Fail 4 times the authentication</li><li>You can check the status of the jail to see the IP being blocked with <code>docker exec -it swag fail2ban-client status jellyfin</code></li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/image-1.png" class="kg-image" alt="Setup SWAG to safely expose your self-hosted applications to the internet" loading="lazy" width="333" height="122"><figcaption>docker exec -it swag fail2ban-client status jellyfin</figcaption></figure><p><br><strong>Few useful commands for Fail2ban</strong></p><ul><li>List all your active jails<br><code>docker exec -it swag fail2ban-client status</code></li><li>List banned IPs for a jail<br><code>docker exec -it swag fail2ban-client status &lt;JAIL_NAME&gt;</code></li><li>Manually unban an IP for a jail<br><code>docker exec -it swag fail2ban-client set &lt;JAIL_NAME&gt; unbanip &lt;IP&gt;</code></li><li>Manually ban an IP for a jail<br><code>docker exec -it swag fail2ban-client set &lt;JAIL_NAME&gt; banip &lt;IP&gt;</code></li></ul><p><strong>&#x1F389; Congrats!</strong><br><strong>You have applied 2 extra layers of security on top of your self-hosted application including Fail2ban! </strong><br><strong>YOU&apos;RE SWAGGY AF!</strong></p><figure class="kg-card kg-image-card"><img src="https://blog.thelazyfox.xyz/content/images/2022/01/swag_3253.gif" class="kg-image" alt="Setup SWAG to safely expose your self-hosted applications to the internet" loading="lazy" width="480" height="270"></figure>]]></content:encoded></item><item><title><![CDATA[How to mount an encrypted Google Drive folder with rclone]]></title><description><![CDATA[The objective of this tutorial is to mount a folder on your server to store content without thinking about storage capacity. Everything on this folder is going to be, on the fly, encrypted and pushed to Google Drive. It comes also with caching!]]></description><link>https://blog.thelazyfox.xyz/how-to-mount-an-encrypted-google-drive-folder-with-rclone/</link><guid isPermaLink="false">62fa86db1e51e000012f25e2</guid><category><![CDATA[Synology]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[GDrive]]></category><category><![CDATA[Rclone]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 29 Nov 2021 06:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-rclone-gdrive.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-rclone-gdrive.png" alt="How to mount an encrypted Google Drive folder with rclone"><p>The objective of this tutorial is to mount a folder on your server to store content without thinking about storage capacity. Everything on this folder is going to be, on the fly, encrypted and pushed to Google Drive. It comes also with caching if you want to use this folder to store your Jellyfin, Emby, Plex or any other media center application library.</p><blockquote><strong>IMPORTANT NOTICE</strong><br>1. <strong>Always backup your <code>rclone.conf</code> file in several secured locations, if lost you will not be able to decrypt your data.</strong><br><strong>2. Google limits the upload on Google Drive to 750 Gb / day. If you exceed it you might get a 24 hours ban.</strong></blockquote><h2 id="haaaave-you-met-rclone">Haaaave you met Rclone?</h2><blockquote>Users call rclone <em>&quot;The Swiss army knife of cloud storage&quot;</em>, and <em>&quot;Technology indistinguishable from magic&quot;</em>.</blockquote><p><em>&quot;Rclone is a command line program to manage files on cloud storage. It is a feature rich alternative to cloud vendors&apos; web storage interfaces. <a href="https://rclone.org/?ref=blog.thelazyfox.xyz#providers">Over 40 cloud storage products</a> support rclone including S3 object stores, business &amp; consumer file storage services, as well as standard transfer protocols.&quot;</em> - <a href="https://rclone.org/?ref=blog.thelazyfox.xyz">rclone.com</a></p><h2 id="install-rclone">Install Rclone</h2><p>Rclone is pretty straight-forward to install with an one-line script. This line also works on a Synology NAS.</p><pre><code class="language-bash">curl https://rclone.org/install.sh | sudo bash</code></pre><p>You can also find all binaries to install it on <a href="https://rclone.org/downloads/?ref=blog.thelazyfox.xyz">rclone.org</a>.</p><blockquote><strong>Note for Synology users</strong>: You need to enable the user home service to use Rclone as its configuration will be stored in <code>/var/services/homes/thelazyfox/.config/rclone/rclone.conf</code>. <br>To enable it, open &quot;Control Panel&quot; &gt; &quot;User &amp; Group&quot; &gt; &quot;Advanced&quot; &gt; &quot;User Home&quot; &gt; &quot;Enable user home service&quot; &gt; &quot;Apply&quot;</blockquote><h2 id="create-a-google-drive-api-application">Create a Google Drive API application</h2><p>Google is providing API keys for almost all its services, including Google Drive.<br>We need to create one for rclone so let&apos;s get started!</p><ol><li>Login on <a href="https://console.developers.google.com/?ref=blog.thelazyfox.xyz">Google API Console</a></li><li>Create a project (Psst, you can call it &quot;Rclone&quot;!)</li><li>Click on &quot;Enable APIs and Services&quot; and search for &quot;Google Drive API&quot;</li><li>Open it and click on &quot;Enable&quot;</li><li>Open the left panel and click on &quot;Credentials&quot;</li><li>Now Click on &quot;Create credentials&quot; &gt; &quot;OAuth client ID&quot;</li><li>If it&apos;s your first time in there, you will need to create and design the <em>OAuth consent screen</em>.<em> </em>It&apos;s the screen which ask for permissions to use your Google Account / Services. You just have to define few parameters like name and type &quot;<strong>External</strong>&quot;</li><li>When you are all set, go back to the &quot;OAuth client ID&quot; creation flow</li><li>Choose your application type (&quot;Desktop app&quot; is fine) and name it (what about &quot;Rclone&quot; again?)</li><li>A pop-in is now displaying your &quot;<strong>Client ID&quot;</strong> and your &quot;<strong>Client secret&quot;</strong>, keep it open, you will need them very soon. You can see them on demand by clicking on the pencil edit icon</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-12.png" class="kg-image" alt="How to mount an encrypted Google Drive folder with rclone" loading="lazy" width="957" height="440" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-12.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-12.png 957w" sizes="(min-width: 720px) 720px"><figcaption>OAuth Client ID created in Google Cloud Platform</figcaption></figure><h2 id="create-a-rclone-remote-drive-not-encrypted">Create a Rclone remote drive (not encrypted)</h2><p>Now it&apos;s time to setup rclone! </p><p>Start by executing the command <code>rclone config</code>.</p><h3 id="create-a-new-remote-drive">Create a new remote drive</h3><pre><code>No remotes found - make a new one
n) New remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config

n/d/r/c/s/q&gt; n
</code></pre><p>Choose <code>n</code> to create a new remote drive and then, give it a name and hit <code>Enter</code>. I chose <strong>&quot;GDrive1&quot;</strong> to know which storage provider it uses and to easily increment it if I need new ones.</p><h3 id="select-storage-type-google-drive">Select storage type: Google Drive</h3><p>As soon as you entered a name, it will show the list of all available storage types. You are looking for &quot;Google Drive&quot;, number 16 when I&apos;m writing this article.<br>Type<code>16</code> and hit <code>Enter</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-13.png" class="kg-image" alt="How to mount an encrypted Google Drive folder with rclone" loading="lazy" width="875" height="630" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-13.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-13.png 875w" sizes="(min-width: 720px) 720px"><figcaption>Partial list of all storage types available in rclone</figcaption></figure><h3 id="enter-your-google-drive-api-application-settings">Enter your Google Drive API application settings</h3><p>Rclone is now asking for the &quot;<strong>Google Application Client Id</strong>&quot; and &quot;<strong>Google Application Client Secret</strong>&quot; you created the step before. Copy/paste them and continue.</p><figure class="kg-card kg-code-card"><pre><code>Option client_id.
Google Application Client Id
Setting your own is recommended.
See https://rclone.org/drive/#making-your-own-client-id for how to create your own.
If you leave this blank, it will use an internal key which is low performance.
Enter a string value. Press Enter for the default (&quot;&quot;).
client_id&gt; this_is_my_client_id
Option client_secret.
OAuth Client Secret.
Leave blank normally.
Enter a string value. Press Enter for the default (&quot;&quot;).
client_secret&gt; this_is_my_client_secret</code></pre><figcaption>Enter your &quot;Google Application Client Id&quot; and &quot;Google Application Client Secret&quot;</figcaption></figure><p>Now you have to select the scope, type <code>1</code> for full access to your Google Drive and hit <code>Enter</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-14.png" class="kg-image" alt="How to mount an encrypted Google Drive folder with rclone" loading="lazy" width="449" height="258"><figcaption>Google Drive&apos;s scope available options in rclone</figcaption></figure><h3 id="choose-a-folder">Choose a folder</h3><p>The next step proposes to choose a specific folder to use into Google Drive.</p><p>I have decided to create a folder called &quot;rclone&quot; into my Google Drive root folder as I&apos;m using Google Drive for other purposes as well. Rclone will require the ID of this folder and it&apos;s super easy to get it, it&apos;s the last part of the URL when your are inside that folder, just after <code>folders/</code>.</p><figure class="kg-card kg-code-card"><pre><code>https://drive.google.com/drive/u/2/folders/1KTYd5OhJ2k-IHLewp1WBquNN9VmwdDNj</code></pre><figcaption>Google Drive folder URL</figcaption></figure><p>Otherwise, if you want to use the root folder of your Google Drive, leave it empty.</p><pre><code>ID of the root folder - leave blank normally.  Fill in to access &quot;Computers&quot; folders. (see docs).
root_folder_id&gt; 1KTYd5OhJ2k-IHLewp1WBquNN9VmwdDNj</code></pre><h3 id="additional-settings-optional">Additional settings (Optional)</h3><p>The next two questions are optional, leave them blank.</p><p>The first one is the &quot;Service Account Credentials&quot;. Google Cloud Platform proposes to use Service account instead of interactive login with your personal account. In this tutorial, I will use my Google account directly. If you want to read more about <a href="https://cloud.google.com/docs/authentication/best-practices-applications?ref=blog.thelazyfox.xyz">authentication best practices</a> or if you want to <a href="https://console.cloud.google.com/iam-admin/serviceaccounts?ref=blog.thelazyfox.xyz">create a service account</a>, everything is available in <a href="https://cloud.google.com/docs/?ref=blog.thelazyfox.xyz">Google Cloud Platform documentation</a>. </p><pre><code>Option service_account_file.
Service Account Credentials JSON file path.
Leave blank normally.
Needed only if you want use SA instead of interactive login.
Leading `~` will be expanded in the file name as will environment variables such as `${RCLONE_CONFIG_DIR}`.
Enter a string value. Press Enter for the default (&quot;&quot;).
service_account_file&gt; </code></pre><p>The second one is advanced configuration, type <code>n</code> and then hit <code>Enter</code> to move to the next step.</p><pre><code>Edit advanced config? (y/n)
y) Yes
n) No (default)
y/n&gt; n</code></pre><h3 id="google-drive-authentication">Google Drive Authentication</h3><p>It&apos;s now time to give rclone the permissions to use your Google Drive account.</p><p>If you are working on a Desktop machine directly, enter <code>y</code> and hit <code>Enter</code> to proceed automatically. Otherwise, if you are connected via SSH like I do, rclone cannot open a web browser to prompt the authentication window, therefore enter <code>n</code> and hit <code>Enter</code> to continue manually.</p><p>A link to Google&apos;s OAuth page is given to you, open it and authorize your rclone application to access your Google Drive account, it will provide you a verification code. Copy/paste the code in the console and hit <code>Enter</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-17.png" class="kg-image" alt="How to mount an encrypted Google Drive folder with rclone" loading="lazy" width="471" height="189"><figcaption>Manual authentication flow</figcaption></figure><h3 id="team-drive">Team drive</h3><p>The next step is to define if you want to use your main Google Drive or a Team Drive. I have no team drive so I type <code>n</code> and hit <code>Enter</code>.</p><pre><code>Configure this as a team drive?
y) Yes
n) No
y/n&gt; n</code></pre><h3 id="final-review">Final review</h3><p>As a final step, rclone gives you an overview of the whole setup and asks to confirm. If you are done, type <code>y</code> and hit <code>Enter</code>.</p><p>At this moment, you are done with the Google Drive configuration into rclone. The next step is about setting up some caching and encryption on top of it. &#x1F510;</p><h2 id="create-the-cache-and-encrypted-rclone-remote-drives">Create the cache and encrypted rclone remote drives</h2><p>You have two options:</p><ol><li>Start the wizard tool a second time with <code>rclone config</code>, choosing option 8 to &quot;cache a remote&quot; and then start it a third time choosing option 12 to &quot;Encrypt/Decrypt a remote&quot;</li><li>Edit your <code>rclone.conf</code> manually and inject the config below inside</li></ol><p>I suggest to use the second option as you are already quite familiar with the wizard!</p><h3 id="edit-rcloneconf">Edit rclone.conf</h3><p>The <code>rclone.conf</code> file is stored in <code>.config/rclone/</code> in your user&apos;s home folder. </p><blockquote>For Synology users, the path is /var/services/homes/your-user/.config/rclone/rclone.conf</blockquote><p>Open <code>rclone.conf</code> file with <code>nano &#xA0;~/.config/rclone/rclone.conf</code> and paste the code of <code>[GDrive1Cache]</code> and <code>[GDrive1Crypt]</code> below your existing <code>GDrive1</code> configuration.</p><p>Name the drives as you wish by changing the text between the brackets but keep in mind the following:</p><ol><li>The caching remote drive is relates to the Google Drive remote drive in its own configuration and so do the encrypted remote drive with the caching remote drive, <strong>ensure the names are correct</strong></li><li><strong>Always keep the <code>:</code> after the drive name</strong></li></ol><figure class="kg-card kg-code-card"><pre><code>[GDrive1]
type = drive
client_id = this_is_my_client_id
client_secret = this_is_my_client_secret
scope = drive
root_folder_id = 1KTYd5OhJ2k-IHLewp1WBquNN9VmwdDNj
token = {&quot;access_token&quot;:&quot;AAA&quot;,&quot;token_type&quot;:&quot;BBB&quot;,&quot;refresh_token&quot;:&quot;CCC&quot;,&quot;expiry&quot;:&quot;DDD&quot;}
team_drive =

[GDrive1Cache]
type = cache
remote = GDrive1:
chunk_size = 50M
info_age = 1h0m0s
chunk_total_size = 10G

[GDrive1Crypt]
type = crypt
remote = GDrive1Cache:
filename_encryption = standard
directory_name_encryption = true</code></pre><figcaption><code>~/.config/rclone/rclone.conf</code></figcaption></figure><p>You are now missing only one thing to complete the configuration, the encryption keys of our encrypted remote drive! </p><h3 id="define-your-encryption-keys">Define your encryption keys</h3><p>To add the encryption keys (password and salt) to your configuration, you need to edit the configuration through <code>rclone config</code>.</p><ol><li>Select <code>Edit existing remote</code></li><li>Choose your encrypted remote drive <code>GDrive1Crypt</code></li><li>Hit <code>n</code> to not change the parameters until you reach the <code>Password or pass phrase for encryption</code>and <code>Password or passphrase for salt</code></li><li>Choose <code>Generate random password</code> for both and save them in a secured location in case of troubles</li><li>Finish and validate your configuration</li></ol><p></p><p>You successfully setup on rclone:</p><ul><li>A Google Drive remote drive</li><li>A cache remote drive</li><li>An encrypted remote drive</li></ul><p>It&apos;s now time to test and start using it! &#x1F468;&#x200D;&#x1F52C;</p><h2 id="mount-your-encrypted-remote-drive">Mount your encrypted remote drive</h2><p>To be able to use it, you need to mount it on your server or Synology NAS. <br>Start by creating a folder where you want to mount it with, for example, <code>mkdir /mnt/GDrive1</code>on your server or <code>mkdir /volume1/rclone/GDrive1</code> on your Synology NAS.</p><h3 id="on-your-server">On your server</h3><p>To ensure your encrypted remote drive is always mount on boot, you need to create a systemd service:</p><ol><li>Create the service file with <code>nano /etc/systemd/system/GDrive1Crypt.service</code></li><li>Copy/Paste the service below and update the correct path to <code>rclone.conf</code></li><li>Save with Ctrl+X and Y</li><li>Reload the list of systemd scripts with <code>sudo systemctl daemon-reload</code></li><li>Start the service with <code>sudo systemctl start GDrive1Crypt.service</code></li><li>Enable it so it runs automatically on boot with <code>sudo systemctl enable GDrive1Crypt.service</code></li></ol><figure class="kg-card kg-code-card"><pre><code>[Unit]
Description=Mount GDrive1Crypt
AssertPathIsDirectory=/mnt/GDrive1
After=networking.service

[Service]
Type=simple
ExecStart=rclone mount --config=/home/thelazyfox/.config/rclone/rclone.conf GDrive1Crypt: /mnt/GDrive1 --allow-other --cache-db-purge --fast-list --poll-interval 10m
ExecStop=/bin/fusermount -u /mnt/GDrive1
Restart=always
RestartSec=10

[Install]
WantedBy=default.target</code></pre><figcaption>/etc/systemd/system/GDrive1Crypt.service</figcaption></figure><h3 id="on-your-synology-nas">On your Synology NAS</h3><p>As it&apos;s not possible to create systemd service on Synology NAS, you need to create a task running on each boot:</p><ol><li>Open &quot;Control Panel&quot;</li><li>Click on &quot;Create&quot; &gt; &quot;Triggered Task&quot; &gt; &quot;User-defined script&quot;</li><li>Define a name, select &quot;Boot-up&quot; as Event and move to the tab &quot;Task Settings&quot;</li><li>In the &quot;Run command&quot; section, enter the script below</li><li>Optional: You can choose to receive the run details if there is an issue to your email address by checking the two boxes and entering your email address</li><li>Click on &quot;OK&quot; to save </li><li>Select your task and click on &quot;Run&quot; </li></ol><figure class="kg-card kg-code-card"><pre><code>rclone mount --config=/var/services/homes/thelazyfox/.config/rclone/rclone.conf GDrive1Crypt: /volume1/rclone/GDrive1 --allow-other --cache-db-purge --fast-list --poll-interval 10m </code></pre><figcaption>Boot-up task to mount rclone drive on Synology NAS</figcaption></figure><h3 id="testing">Testing </h3><p>You can now copy or create files in this folder and you should see them <strong>encrypted </strong>on your Google Drive! &#x1F680;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-18.png" class="kg-image" alt="How to mount an encrypted Google Drive folder with rclone" loading="lazy" width="687" height="166" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-18.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-18.png 687w"><figcaption>Encrypted file and folder on Google Drive</figcaption></figure><p>&#x1F389; Congrats! You have mounted your first encrypted and cached remote drive with rclone and Google Drive! Rclone is a very powerful tool with a lot of others interesting features!</p><p>If for any reason you can&apos;t access to your GDrive remote, refresh your tocken with <code>rclone config reconnect GDrive1:</code></p><p><em>Thanks to </em><a href="https://upandclear.org/?ref=blog.thelazyfox.xyz"><em>Aerya</em></a><em> &amp; </em><a href="https://mrpsycho.pl/?ref=blog.thelazyfox.xyz"><em>MrPsycho</em></a><em> for support and resources!</em></p><p> <br>Feel free to share your setup and experiences with rclone on Discord!</p>]]></content:encoded></item><item><title><![CDATA[FreshRSS: A free self-hosted RSS aggregator]]></title><description><![CDATA[You waste too much time to visit every single day your favorite websites? 
You care about privacy and don't want any website to know your preferences? 
You don't want to miss any GitHub release from your favorites software? 
No worries! FreshRSS can solve all your problems! ]]></description><link>https://blog.thelazyfox.xyz/freshrss-a-free-self-hosted-rss-aggregator/</link><guid isPermaLink="false">62fa86db1e51e000012f25df</guid><category><![CDATA[Docker]]></category><category><![CDATA[Self-hosted]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 29 Nov 2021 06:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-freshrss.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-freshrss.png" alt="FreshRSS: A free self-hosted RSS aggregator"><p>You waste too much time visiting every single day your favorite websites? <br>You care about privacy and don&apos;t want any website to know your preferences? <br>You don&apos;t want to miss any GitHub release from your favorites software? <br>No worries! <a href="https://freshrss.org/?ref=blog.thelazyfox.xyz">FreshRSS</a> can solve all your problems! </p><h2 id="installation">Installation</h2><p>Once again, nothing very complex here, the docker image is provided by <a href="https://www.linuxserver.io/?ref=blog.thelazyfox.xyz">LinuxServer</a> &#x1F9A6;.</p><p>First thing first, you need to create a folder for this project and put inside your <em>docker-compose.yml</em> file.</p><pre><code class="language-yaml">version: &quot;2.3&quot;
services:
  freshrss:
    container_name: freshrss
    image: lscr.io/linuxserver/freshrss:latest
    restart: unless-stopped
    ports:
      - 8082:80
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Paris
    volumes:
      - /home/thelazyfox/docker/freshrss/config:/config</code></pre><p><em>Please remember to create all folders and files before trying to launch your docker-compose.</em></p><p>How does it look like on my server:</p><pre><code>freshrss/
      |_ docker-compose.yaml
      |_ config/</code></pre><p>And now, you just have to launch your docker-compose file with the following command <code>cd freshrss &amp;&amp; docker-compose up -d</code></p><h2 id="features">Features</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-1.png" class="kg-image" alt="FreshRSS: A free self-hosted RSS aggregator" loading="lazy" width="940" height="755" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-1.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-1.png 940w" sizes="(min-width: 720px) 720px"><figcaption>Overview of the main stream from FreshRSS</figcaption></figure><p>In FreshRSS, you can manage everything you need around your RSS feeds including:</p><ul><li>Categories</li><li>Labels</li><li>Archiving strategy</li><li>What to display on your main feed</li><li>Multi users is supported so you can share it with your family</li><li>Customize the theme</li><li>Add addons</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-2.png" class="kg-image" alt="FreshRSS: A free self-hosted RSS aggregator" loading="lazy" width="956" height="453" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-2.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-2.png 956w" sizes="(min-width: 720px) 720px"><figcaption>Add a new feed is easy, just copy paste the website&apos;s URL and follow the steps</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-3.png" class="kg-image" alt="FreshRSS: A free self-hosted RSS aggregator" loading="lazy" width="1201" height="824" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-3.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/image-3.png 1000w, https://blog.thelazyfox.xyz/content/images/2021/11/image-3.png 1201w" sizes="(min-width: 720px) 720px"><figcaption>Define your archiving policy and customize it per website if needed</figcaption></figure><p><u>Tips:</u> If you want to follow the releases of your favorite applications, just enter the releases GitHub page of the project. Example: <a href="https://github.com/xbmc/xbmc/releases/?ref=blog.thelazyfox.xyz">https://github.com/xbmc/xbmc/releases/</a></p><p>I let you discover all features, feel free to share your favorites!</p>]]></content:encoded></item><item><title><![CDATA[Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker]]></title><description><![CDATA[This article will detail all the hardware and software I have used to monitor the temperatures from a fridge and freezer to ensure they never go crazy. It comes with notifications as well in case of issues.]]></description><link>https://blog.thelazyfox.xyz/monitor-temperature-with-a-raspberry-pi-and-grafana-influxdb-on-docker/</link><guid isPermaLink="false">62fa86db1e51e000012f25e0</guid><category><![CDATA[Docker]]></category><category><![CDATA[Self-hosted]]></category><category><![CDATA[RaspberryPi]]></category><category><![CDATA[Grafana]]></category><category><![CDATA[InfluxDb]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 22 Nov 2021 06:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-monitor-temperature.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-monitor-temperature.png" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker"><p>This article will detail all the hardware and software I have used to monitor the temperatures from a fridge and freezer to ensure they never go crazy. It comes with notifications as well in case of issues.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-4.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="1111" height="547" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-4.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/image-4.png 1000w, https://blog.thelazyfox.xyz/content/images/2021/11/image-4.png 1111w" sizes="(min-width: 720px) 720px"><figcaption>Our target: a neat dashboard in Grafana</figcaption></figure><h2 id="first-things-first-the-hardware">First things first, the hardware!</h2><p>To be able to get this result, you will need:</p><ol><li>A <a href="https://www.raspberrypi.org/?ref=blog.thelazyfox.xyz">Raspberry Pi</a> with <a href="https://www.raspbian.org/?ref=blog.thelazyfox.xyz">Raspbian</a>, it will not consume a lot of power and CPU so Model 1 or 2 are enough</li><li>One or more <a href="https://www.amazon.com/Maxmoral-Stainless-Encapsulation-Waterproof-Temperature/dp/B07PLZRP1H/ref=sr_1_11?crid=2EFE54E7UP3MK&amp;dchild=1&amp;keywords=DS18B20&amp;qid=1635767538&amp;qsid=134-3009848-0928735&amp;sprefix=ds18b20+%2Caps%2C155&amp;sr=8-11&amp;sres=B00M1PM55K%2CB07MR71WVS%2CB012C597T0%2CB08RJ59BDJ%2CB08W27W7LJ%2CB08V93CTM2%2CB097P25DYD%2CB07PLZRP1H%2CB01LY53CED%2CB07MB1J43W%2CB08T1MLWK2%2CB081VCZPWV%2CB07XNXYNWG%2CB085WBVZHN%2CB00SK698G8%2CB0859XBHZK&amp;ref=blog.thelazyfox.xyz">DS18B20 </a>sensors (I&apos;m using 2), depending on how many temperatures you want to capture. They are waterproof and coming with a 2 meters long wire so you can keep your Raspberry Pi far away and safe</li><li>A <a href="https://www.sciencebuddies.org/science-fair-projects/references/how-to-use-a-breadboard?ref=blog.thelazyfox.xyz">breadboard</a>, it will make your testing and wiring much easier</li><li>A 4.7 kOhm resistor</li><li>A server to run Docker with Grafana and influxDB, the Raspberry Pi&apos;s job is only about capture and push of the data to InfluxDB. You can also run everything on a Raspberry Pi but you will need a more performant one</li></ol><h2 id="hardware-installation">Hardware installation</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-5.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="335" height="367"><figcaption>Raspberry, resistor and sensors (Source: circuits.dk)</figcaption></figure><ol><li>As a first step, shut down your Raspberry Pi and plug the sensors and resistor accordingly to the schema above</li><li>Open raspi-config with <code>sudo raspi-config</code></li><li>Select <code>Interfacing Options</code></li><li>Highlight <code>1-wire</code> and set it to <code>Yes</code> then confirm with <code>Select</code></li><li>Reboot your Raspberry Pi with <code>sudo reboot</code></li></ol><h2 id="sensors-configuration">Sensors configuration</h2><p>Now we will check if the Raspberry Pi recognize the sensors and can capture temperature</p><h3 id="sensors-requirements">Sensors requirements</h3><ol><li>You first need to register all the sensors you have plugged to the GPIO4, run <code>sudo modprobe w1-gpio</code> so the Raspberry Pi is aware of it</li><li>Now, tell to the Raspberry Pi it can measure temperature on the 1-Wire system with <code>sudo modprobe w1-therm</code></li><li>It&apos;s important to load these 2 modules automatically on next boot. For that purpose, edit the modules file with <code>sudo nano /etc/modules</code> and add inside <code>w1-gpio</code>and <code>w1-therm</code> like below</li><li>Save with Ctrl+X and Y</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-7.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="533" height="121"><figcaption>/etc/modules</figcaption></figure><h3 id="testing-the-sensors">Testing the sensors</h3><p>You need to ensure the sensors are capturing the temperature correctly.</p><ol><li>Browse the 1-wire devices folder with <code>cd /sys/bus/w1/devices</code></li><li>List all devices with <code>ls</code>. The DS18B20 sensors start with <code>28-</code> followed by the serial number of the sensor</li><li>Enter in one of the sensors directory and read the file <code>w1_slave</code>with <code>cd 28-yyyy &amp;&amp; cat w1_slave </code> <em>(replace yyyy by your sensor&apos;s serial number)</em></li><li>The value you will see at the end of the second line is the current temperature in degrees Celcius <em>(you need to divide it by 1000, t=-16812 means -16.812&#xB0;C)</em></li><li>The first line contain a <a href="https://en.wikipedia.org/wiki/Cyclic_redundancy_check?ref=blog.thelazyfox.xyz">CRC</a>, if the value is not YES, you might have a defect in your sensor or in its wiring</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-6.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="442" height="146"><figcaption>Checking the sensor value</figcaption></figure><h2 id="prepare-influxdb-and-grafana-to-receive-the-data">Prepare InfluxDB and Grafana to receive the Data </h2><h3 id="build-the-stack">Build the stack</h3><p>Let&apos;s build Grafana and InfluxDB as a docker-compose stack.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &quot;2.3&quot;
services:
  influxdb:
    image: influxdb:1.8-alpine
    container_name: monitoring-temperature-influxdb
    restart: always
    ports:
      - &quot;8083:8083&quot;
      - &quot;8086:8086&quot;
      - &quot;8090:8090&quot;
    environment:
      - PUID=1026
      - PGID=1000
      - TZ=Europe/Paris
    env_file:
      - /home/thelazyfox/docker/monitoring-temperature/influxdb/env.influxdb
    volumes:
      - /home/thelazyfox/docker/monitoring-temperature/influxdb/data:/var/lib/influxdb

  grafana:
    image: grafana/grafana
    container_name: monitoring-temperature-grafana
    restart: always
    ports:
      - &quot;3000:3000&quot;
    links:
      - influxdb
    volumes:
      - /home/thelazyfox/docker/monitoring-temperature/grafana/data:/var/lib/grafana
    environment:
      - PUID=1026
      - PGID=1000
      - TZ=Europe/Paris
      - GF_RENDERING_SERVER_URL=http://10.1.1.11:8123/render
      - GF_RENDERING_CALLBACK_URL=http://10.1.1.11:3000/
      - GF_LOG_FILTERS=rendering:debug
      - GF_INSTALL_PLUGINS=grafana-clock-panel,briangann-gauge-panel,natel-plotly-panel,grafana-simple-json-datasource
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_NAME=Home
    labels:

  grafana-image-renderer:
    image: grafana/grafana-image-renderer
    container_name: monitoring-temperature-grafana-image-renderer
    restart: always
    ports:
      - &quot;8123:8081&quot;
    environment:
      - PUID=1026
      - PGID=1000
      - TZ=Europe/Paris
      - IGNORE_HTTPS_ERRORS=true
      - LOG_LEVEL=debug
      - RENDERING_DUMPIO=true
      - RENDERING_MODE=default
      - ENABLE_METRICS=true</code></pre><figcaption>monitoring-temperature/docker-compose.yml</figcaption></figure><p><em>Please remember to create all folders and files before trying to launch your docker-compose.</em></p><p>How does it look like on my server:</p><pre><code>monitoring-temperature/
      |_ docker-compose.yaml
      |_ influxdb/
          |_ env.influxdb/ 
          |_ data/ 
      |_ grafana/          
          |_ data/ </code></pre><p>Few notes regarding the stack:</p><ul><li><code>grafana-image-renderer</code> is used to integrate charts screenshot in the notifications</li><li><code>grafana-image-renderer</code> IP and port must be updated into <code>GF_RENDERING_SERVER_URL</code> environment variable of grafana service</li><li>Grafana&apos;s IP and port must be updated into <code>GF_RENDERING_CALLBACK_URL</code> environment variable of grafana service</li></ul><p>And now, launch your docker-compose file with the following command <code>cd monitoring-temperature &amp;&amp; docker-compose up -d</code></p><h3 id="configure-influxdb">Configure InfluxDB</h3><p>It&apos;s time to create your InfluxDB database.</p><ol><li>Enter in your container with <code>docker exec -it monitoring-temperature-influxdb bash</code></li><li>Open InfluxDB shell with <code>influx</code></li><li>Create your database named &quot;temp_logger_db&quot; with <code>CREATE DATABASE temp_logger_db</code></li><li>Confirm it was successfully created by running <code>SHOW DATABASES</code></li><li>You can now enter <code>exit</code> and <code>exit</code> again to resume</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-9.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="674" height="83" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-9.png 600w, https://blog.thelazyfox.xyz/content/images/2021/11/image-9.png 674w"><figcaption>Database creation in InfluxDB via CLI</figcaption></figure><h3 id="configure-grafana">Configure Grafana</h3><p>Grafana is accessible on port 3000, login with the default account <code>admin:admin</code> to link InfluxDB as a data source into Grafana.</p><ol><li>Open Settings</li><li>Select &quot;Data Sources&quot; tab</li><li>Click on &quot;Add data source&quot;</li><li>Search and select &quot;InfluxDB&quot;</li><li>Configure as below <em>(Server URL, Database name and credentials)</em></li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-8.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="1040" height="955" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-8.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/image-8.png 1000w, https://blog.thelazyfox.xyz/content/images/2021/11/image-8.png 1040w" sizes="(min-width: 720px) 720px"><figcaption>InfluxDB configuration in Grafana</figcaption></figure><h2 id="capture-data-with-python3-on-raspberry-pi">Capture data with Python3 on Raspberry Pi</h2><p>InfluxDB is ready and Grafana is connected to InfluxDB. Now it&apos;s time to go back on your Raspberry Pi to capture sensors&apos; data from the files we saw before and inject them into InfluxDB.</p><h3 id="install-dependencies">Install dependencies</h3><p>You need to install python3 and its influx library with <code>sudo apt-get install python3 python3-pip &amp;&amp; pip install influxdb</code></p><h3 id="create-the-script">Create the script</h3><ol><li>To capture the data, you have to create the python script below with <code>sudo nano templogger.py</code></li><li>Replace the sensors serial numbers on line 17 and integrate your InfluxDB information on line 20 to 23 and save</li><li>Test the script by running <code>/usr/bin/python3 /home/pi/templogger.py -db=temp_logger_db -sn=data</code></li><li>You should see your temperatures</li></ol><pre><code class="language-python"># -*- coding: utf-8 -*-
import os
import glob
import argparse
import time
import datetime
import sys
from influxdb import InfluxDBClient

os.system(&apos;modprobe w1-gpio&apos;)
os.system(&apos;modprobe w1-therm&apos;)

base_dir = &apos;/sys/bus/w1/devices/&apos;
device_folders = glob.glob(base_dir + &apos;28*&apos;)

# List of temperature sensors from base_dir. Add as much as you want.
sensors=[&apos;28-012032d0174e&apos;, &apos;28-012032e0a6f0&apos;]

# InfluxDB parameters
host = &quot;10.1.1.11&quot;
port = 8086
user = &quot;root&quot;
password = &quot;root&quot;

def get_args():
    parser = argparse.ArgumentParser(description=&apos;Push Celsius temperatures from DS18B20 sensors to an Influx database.&apos;)
    parser.add_argument(
        &apos;-db&apos;, &apos;--database&apos;, type=str, help=&apos;Database name&apos;, required=True)
    parser.add_argument(
        &apos;-sn&apos;, &apos;--session&apos;, type=str, help=&apos;Session&apos;, required=True)
    now = datetime.datetime.now()
    parser.add_argument(
        &apos;-rn&apos;, &apos;--run&apos;, type=str, help=&apos;Run number&apos;, required=False,
        default=now.strftime(&quot;%Y%m%d%H%M&quot;))
    
    args = parser.parse_args()
    db_name = args.database
    run_no = args.run
    session = args.session
    return db_name, session, run_no

def read_temperature_raw(device_file):
    f = open(device_file, &apos;r&apos;)
    lines = f.readlines()
    f.close()
    return lines

def read_temperature(device_file):
    lines = read_temperature_raw(device_file)

    # Wait for the file to be available with a valid CRC
    while (not lines) or (lines[0].strip()[-3:] != &apos;YES&apos;):
        time.sleep(0.2)
        print (&quot;Retrying...&quot;)
        lines = read_temperature_raw(device_file)

    # Get the temperature value from the file
    equals_pos = lines[1].find(&apos;t=&apos;)

    # Convert to Celcius and round to 1 decimal
    if equals_pos != -1:
        temperature_string = lines[1][equals_pos+2:]
        temperature = float(temperature_string) / 1000.0
        temperature = round(temperature, 1)

    return temperature

def get_data_points():
    # Go through all sensors to get the temperature
    for sensor in range (len(sensors)):
        device_file=device_folders[sensor]+ &apos;/w1_slave&apos;
        sensors[sensor] = read_temperature(device_file)
        print (device_file,sensor,sensors[sensor])
    timestamp=datetime.datetime.utcnow().isoformat()

    # Create Influxdb datapoints
    datapoints = [
        {
            &quot;measurement&quot;: session,
            &quot;tags&quot;: {&quot;runNum&quot;: run_no,},
            &quot;time&quot;: timestamp,
            &quot;fields&quot;: {&quot;temperature 1&quot;:sensors[0],&quot;temperature 2&quot;:sensors[1]}
        }
        ]
    return datapoints

# Get values from arguments
db_name, session, run_no = get_args()
print (&quot;Session: &quot;, session)
print (&quot;Run No: &quot;, run_no)
print (&quot;DB name: &quot;, db_name)

# Initialize the Influxdb client
client = InfluxDBClient(host, port, user, password, db_name)

# Collecting and pushing data
datapoints=get_data_points()
bResult=client.write_points(datapoints)
print(&quot;Write points {0} Bresult:{1}&quot;.format(datapoints, bResult))</code></pre><h3 id="run-it-automatically-as-a-cron-job">Run it automatically as a cron job</h3><p>You want to capture the data as soon as the Raspberry Pi is turned on and on a regular basis, for that purpose, we need to create a cronjob.</p><ol><li>Open the crontab with <code>crontab -e</code></li><li>Add this line to run the script every minute: <code>* * * * * /usr/bin/python3 /home/pi/templogger.py -db=temp_logger_db -sn=data</code> <br>If you need help with crontab to change the frequency, you can use <a href="https://crontab.cronhub.io/?ref=blog.thelazyfox.xyz">cronhub</a></li><li>Update the script&apos;s path and the database name if needed</li><li>Save with Ctrl+X and Y</li><li>It&apos;s over!</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-11.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="497" height="124"><figcaption>Crontab file</figcaption></figure><h3 id="run-it-as-a-service-with-systemd">Run it as a service with systemd</h3><p>As an alternative, you can also manage your data capture with a systemd service. First you will need to slightly alter the script.</p><p><strong>Adapt the script</strong></p><p>Replace the section &quot;Collecting and pushing data&quot; from the previous script with this one:</p><figure class="kg-card kg-code-card"><pre><code class="language-python"># Collecting and pushing data
try:
     while True:
        datapoints=get_data_points()
        bResult=client.write_points(datapoints)
        print(&quot;Write points {0} Bresult:{1}&quot;.format(datapoints,bResult))
        time.sleep(60)
       
except KeyboardInterrupt:
    print (&quot;Program stopped by keyboard interrupt [CTRL_C].&quot;)</code></pre><figcaption>Replace the value &quot;60&quot; by any value in seconds, it will define the data capture cadence</figcaption></figure><p><strong>Create and start the service</strong></p><ol><li>Create the file with <code>nano /etc/systemd/system/templogger.service</code></li><li>Copy/Paste the service below and update the script&apos;s path and the database name if needed</li><li>Save with Ctrl+X and Y</li><li>Reload the list of systemd scripts with <code>sudo systemctl daemon-reload</code></li><li>Start the service with <code>sudo systemctl start templogger.service</code></li><li>Enable it so it runs automatically on boot with <code>sudo systemctl enable templogger.service</code></li></ol><figure class="kg-card kg-code-card"><pre><code>[Unit]
Description=Temp Logger
After=multi-user.target

[Service]
Restart=on-failure
RestartSec=5s
Type=idle
User=pi
Group=pi
ExecStart=/usr/bin/python3 /home/pi/templogger.py -db=temp_logger_db -sn=data

[Install]
WantedBy=multi-user.target

</code></pre><figcaption>/etc/systemd/system/templogger.service</figcaption></figure><p><br>&#x270C;&#x1F3FC; By crontab or by service, your temperatures are now captured every 60 seconds in InfluxDB! You can now start building your Grafana Dashboard.</p><h2 id="start-building-your-grafana-dashboard">Start building your Grafana Dashboard</h2><ol><li>Create a new Dashboard</li><li>Add a new Panel and configure the Query as below</li><li>Source <code>InfluxDB</code> from <code>default</code>, the dataset is <code>data</code> <em>(you can customize it by changing the value of <code>-sn</code> in the command)</em></li><li>Select <code>field(temperature1)</code> <em>(you will see one value per sensor)</em></li><li>Format as <code>Time series</code></li><li>Save</li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image-10.png" class="kg-image" alt="Monitor temperature with a Raspberry Pi and Grafana/InfluxDB on Docker" loading="lazy" width="1514" height="924" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image-10.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/image-10.png 1000w, https://blog.thelazyfox.xyz/content/images/2021/11/image-10.png 1514w" sizes="(min-width: 720px) 720px"><figcaption>Panel configuration in Grafana</figcaption></figure><p>&#x1F389; Congrats! Your Raspberry Pi is now capturing temperatures and pushing it in an InfluxDB available through Grafana! <br>Feel free to share your Dashboards and alerts on Discord!</p><hr><p>Since I initially wrote this article, I have printed my own board to replace the breadboard. I&apos;ve designed it on EasyEDA. I put it for download below, feel free to use it and print it with jlcpcb.com.</p>
        <div class="kg-card kg-file-card kg-file-card-medium">
            <a class="kg-file-card-container" href="https://blog.thelazyfox.xyz/content/files/2022/05/PCB_PCB_Pi_Temperature_Monitor_TheLazyFox.json" title="Download" download>
                <div class="kg-file-card-contents">
                    <div class="kg-file-card-title">PCB PCB Pi Temperature Monitor TheLazyFox</div>
                    
                    <div class="kg-file-card-metadata">
                        <div class="kg-file-card-filename">PCB_PCB_Pi_Temperature_Monitor_TheLazyFox.json</div>
                        <div class="kg-file-card-filesize">6 KB</div>
                    </div>
                </div>
                <div class="kg-file-card-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><defs><style>.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}</style></defs><title>download-circle</title><polyline class="a" points="8.25 14.25 12 18 15.75 14.25"/><line class="a" x1="12" y1="6.75" x2="12" y2="18"/><circle class="a" cx="12" cy="12" r="11.25"/></svg>
                </div>
            </a>
        </div>
        <p></p>]]></content:encoded></item><item><title><![CDATA[How to create healthchecks for Docker containers]]></title><description><![CDATA[How to create and manage healthchecks in your Docker compose stack and restart your containers if they are unhealthy.]]></description><link>https://blog.thelazyfox.xyz/how-to-create-healthchecks-for-docker/</link><guid isPermaLink="false">62fa86db1e51e000012f25e1</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Sat, 13 Nov 2021 14:48:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-docker-healthcheck.png" medium="image"/><content:encoded><![CDATA[<h2 id="what-is-an-healthcheck">What is an healthcheck?</h2><img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-docker-healthcheck.png" alt="How to create healthchecks for Docker containers"><p>Docker provides natively an healthcheck system, when a container has an healthcheck configured, it comes with an <em>health status</em> in addition to its normal status. This status is initially <code>starting</code>. Whenever a health check passes, it becomes <code>healthy</code> (whatever state it was previously in). After a certain number of consecutive failures, it becomes <code>unhealthy</code>.</p><p>The <code>HEALTHCHECK</code> instruction tells Docker how to test a container to check that it is still working. This can detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server process is still running.</p><p>Nevertheless, a huge amount of Docker container providers don&apos;t implement this feature. </p><p>No worries, there is a way to integrate an healthcheck in your docker compose file and this is the purpose of this post! &#x1F680;</p><h2 id="healthchecks-with-docker-compose">Healthchecks with Docker Compose</h2><p>This is how it looks like in your docker-compose.yml file.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">healthcheck:
  test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http://localhost&quot;]
  interval: 1m30s
  timeout: 10s
  retries: 3
  start_period: 40s
</code></pre><figcaption>healthcheck in docker-compose</figcaption></figure><p><code>interval</code>, <code>timeout</code> and <code>start_period</code> are self explanatory. <code>interval</code> is the frequency the test is run, <code>timeout</code> is the time allocated to run the test before its considered as failed and <code>start_period</code> , the time to wait before the first test is started to give time to your container to start. They are specified as <a href="https://docs.docker.com/compose/compose-file/compose-file-v3/?ref=blog.thelazyfox.xyz#specifying-durations">durations</a>. <code>retries</code>is the number of tentatives before considering the test as failed. </p><p>Now let&apos;s talk about <code>test</code>, this is the actual test docker is going to execute to determine if your docker is <strong>healthy </strong>or <strong>unhealthy</strong>. It must be either a string or a list. If it&apos;s a string, just put the full command. If it&#x2019;s a list, the first item must be <code>NONE</code>, <code>CMD</code> or <code>CMD-SHELL</code>. </p><ul><li><code>NONE</code>, no healthcheck will run <strong>including</strong> the one in the container if there is one</li><li><code>CMD</code>, it must be followed by the command and all its argument as part of the list</li><li><code>CMD-SHELL</code>, &#xA0;followed by a string it&#x2019;s equivalent to just specifying &#xA0;that string</li></ul><p>Let&apos;s go through some examples to hit the local webserver to see if its running correctly:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># CMD option
test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-f&quot;, &quot;http://localhost&quot;]

# CMD-SHELL option
test: [&quot;CMD-SHELL&quot;, &quot;curl -f http://localhost || exit 1&quot;]

# String option
test: &quot;curl -f https://localhost || exit 1&quot;</code></pre><figcaption>Different test options</figcaption></figure><h2 id="healthcheck-and-dependencies">Healthcheck and dependencies</h2><p>You probably already use <code>depends_on</code> to ensure some containers are started before launching another one. </p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">depends_on:
   - mycontainer
   
# OR 

depends_on:
   mycontainer:
      condition: service_started</code></pre><figcaption>Wait for mycontainer to be started</figcaption></figure><p>Now you can wait until the container turns healthy! &#x270C;&#x1F3FC;</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">depends_on:
  mycontainer:
    condition: service_healthy</code></pre><figcaption>Wait for mycontainer to be healthy</figcaption></figure><h2 id="and-now-what-if">And now, what if?</h2><h3 id="my-container-is-not-a-webserver">My container is not a webserver</h3><p>When your container is not running a webserver, you will need to define what command to run to ensure its healthy.</p><p>Some containers contain interesting command like <code>postgre</code>which comes with <code>pg_isready</code>to know if the database server is up and running. Check the <a href="https://www.postgresql.org/docs/10/app-pg-isready.html?ref=blog.thelazyfox.xyz">documentation </a>if you want to customize it.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U user&quot;]</code></pre><figcaption>Check if postgre server is up and running</figcaption></figure><p>With <code>influxdb</code>, you can list all databases which will indeed confirm the up and running state of your container.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">test: [&quot;CMD-SHELL&quot;, &quot;influx -execute &apos;SHOW DATABASES&apos; || exit 1&quot;]
</code></pre><figcaption>Check if influxdb server is up and running</figcaption></figure><h3 id="my-container-doesnt-have-curl">My container doesn&apos;t have curl</h3><p>Some of the containers running a webserver don&apos;t contain <code>curl</code>, no worries, you can still use <code>wget</code>.</p><pre><code class="language-yaml">test: [&quot;CMD-SHELL&quot;, &quot;wget --no-verbose --tries=1 --spider --no-check-certificate http://localhost/ || exit 1&quot;]</code></pre><h3 id="my-container-uses-another-containers-network">My container uses another container&apos;s network</h3><p>And finally, what if you are <a href="https://blog.thelazyfox.xyz/routing-docker-traffic-through-a-vpn-container/">routing a container traffic through a vpn container</a>? </p><p>In this case, it&apos;s important to hit the local webserver through the vpn container in your healthcheck&apos;s test. </p><p>In the example below, I&apos;m testing the internal port of the container through the VPN IP address. By doing this, you ensure your container will turn <strong>unhealthy </strong>if your VPN container is restarting or just down.</p><pre><code class="language-yaml">version: &quot;2.3&quot; 
services: 
  vpn:
    container_name: my-vpn
    [...]
    networks:
      default:
        ipv4_address: 172.21.0.2
    ports:
      - 8888:1111
    [...]

  mycontainer:
    container_name: my-container
    [...]
    depends_on:
     - vpn
    network_mode: &quot;service:vpn&quot;
    [...]
    healthcheck:
      test: [&quot;CMD-SHELL&quot;, &quot;curl --fail http://172.21.0.2:1111/ || exit 1&quot;]
      start_period: 120s
      interval: 60s
      timeout: 10s
      retries: 3

networks:
  default:
    external:
      name: my-vpn-network</code></pre><h3 id="my-container-is-unhealthy">My container is unhealthy</h3><p>If your container is unhealthy, you can use some of the containers able to restart unhealthy ones automatically. I recommend <a href="https://github.com/willfarrell/docker-autoheal?ref=blog.thelazyfox.xyz">willfarrell/docker-autoheal</a> or <a href="https://github.com/qdm12/deunhealth?ref=blog.thelazyfox.xyz">qdm12/deunhealth</a>. I personnaly use the second one, written in Go like Docker it directly uses the Docker API.</p><p>The docker-compose.yml file is super simple to setup, no volume to map.</p><pre><code class="language-yaml">version: &quot;3.7&quot;
services:
  deunhealth:
    image: qmcgaw/deunhealth:latest
    container_name: deunhealth
    network_mode: &quot;none&quot;
    environment:
      - LOG_LEVEL=info
      - HEALTH_SERVER_ADDRESS=127.0.0.1:9999
      - TZ=Europe/Zurich
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
</code></pre><p>&#x1F389; Now it&apos;s your turn to healthcheck all your containers!</p>]]></content:encoded></item><item><title><![CDATA[Speedtest-Tracker: Continuously track your internet speed]]></title><description><![CDATA[Today, I'm presenting to you an interesting project from henrywhitaker3 on GitHub called Speedtest-Tracker. In few words, this is a self-hosted and always-on speed test solution.]]></description><link>https://blog.thelazyfox.xyz/speedtest-tracker-continuously-track-your-internet-speed/</link><guid isPermaLink="false">62fa86db1e51e000012f25de</guid><category><![CDATA[Self-hosted]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 08 Nov 2021 06:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-speedtest-tracker-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-speedtest-tracker-1.png" alt="Speedtest-Tracker: Continuously track your internet speed"><p>Today, I&apos;m presenting to you an interesting project from <a href="https://github.com/henrywhitaker3/Speedtest-Tracker?ref=blog.thelazyfox.xyz">henrywhitaker3 on GitHub</a> called Speedtest-Tracker. In few words, this is a self-hosted and always-on speed test solution.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/image.png" class="kg-image" alt="Speedtest-Tracker: Continuously track your internet speed" loading="lazy" width="1918" height="797" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/image.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/image.png 1000w, https://blog.thelazyfox.xyz/content/images/size/w1600/2021/11/image.png 1600w, https://blog.thelazyfox.xyz/content/images/2021/11/image.png 1918w" sizes="(min-width: 720px) 720px"><figcaption>Thanks to Speedtest-Tracker, you can easily identify and get alerted when you internet connection is experiencing issues</figcaption></figure><p>It uses <a href="https://www.speedtest.net/apps/cli?ref=blog.thelazyfox.xyz">Ookla&apos;s Speedtest cli</a> to exectue the speedtest and uses <a href="https://www.chartjs.org/?ref=blog.thelazyfox.xyz">Chart.js</a> to display awesome charts with the results. The application is developed with <a href="https://laravel.com/?ref=blog.thelazyfox.xyz">Laravel</a> for the back-end and the front-end uses <a href="https://reactjs.org/?ref=blog.thelazyfox.xyz">React</a>.</p><h2 id="installation">Installation</h2><p>Speedtest-Tracker is a <a href="https://hub.docker.com/r/henrywhitaker3/speedtest-tracker?ref=blog.thelazyfox.xyz">Docker image</a>, so we will use docker-compose as usual. Like <a href="https://blog.thelazyfox.xyz/the-lounge-the-self-hosted-web-irc-client/">TheLounge</a>, the file is straight-forward, you need to map your configuration folder and that&apos;s it.<br>Please note the environment variable <code>OOKLA_EULA_GDPR=true</code> which is mandatory to accept Ookla&apos;s <a href="https://www.speedtest.net/about/eula?ref=blog.thelazyfox.xyz">EULA</a> and privacy agreements in order to use this container.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &apos;3&apos;
services:
  speedtest:
    container_name: speedtest-tracker
    image: henrywhitaker3/speedtest-tracker:latest
    restart: unless-stopped
    ports:
      - 8765:80
    volumes:
      - ./config:/config
    environment:
      - TZ=Europe/Zurich
      - PUID=1026
      - PGID=100
      - OOKLA_EULA_GDPR=true
    logging:
      driver: &quot;json-file&quot;
      options:
        max-file: &quot;10&quot;
        max-size: &quot;200k&quot;
</code></pre><figcaption>speedtracker/docker-compose.yml</figcaption></figure><h2 id="features">Features</h2><ul><li>It runs a speedtest automatically every hour</li><li>Charts to see the history of your speedtests</li><li>Backup/restore any data in JSON or CSV format</li><li>Notifications through Slack, Discord or Telegram</li><li>Organizr integration</li><li><a href="https://healthchecks.io/?ref=blog.thelazyfox.xyz" rel="nofollow">healthchecks.io</a> integration</li><li>InfluxDB integration (currently v1 only, v2 is a WIP)</li></ul><h2 id="it-doesnt-work">It doesn&apos;t work?</h2><p>Some users have reported issues to accept the Ookla&apos;s <a href="https://www.speedtest.net/about/eula?ref=blog.thelazyfox.xyz">EULA</a> and privacy agreements. If you can&apos;t get any speedtest results, try this:</p><ol><li>Execute <code>docker exec -it speedtest-tracker bash</code> to get into the container</li><li>Run <code>php /config/www/artisan speedtest:eula</code> to force the acceptation </li><li>Restart your container with <code>docker restart speedtest-tracker</code></li></ol>]]></content:encoded></item><item><title><![CDATA[The Lounge: The self-hosted web IRC client]]></title><description><![CDATA[The Lounge is the perfect solution for you if you are looking for an "Always connected" and self-hosted IRC client.]]></description><link>https://blog.thelazyfox.xyz/the-lounge-the-self-hosted-web-irc-client/</link><guid isPermaLink="false">62fa86db1e51e000012f25dd</guid><category><![CDATA[Self-hosted]]></category><category><![CDATA[Docker]]></category><category><![CDATA[VPN]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 01 Nov 2021 07:30:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-thelounge-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-thelounge-1.png" alt="The Lounge: The self-hosted web IRC client"><p>The Lounge is the perfect solution for you if you are looking for an &quot;Always connected&quot; and self-hosted IRC client.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/11/thelounge-theme-new-morning.png" class="kg-image" alt="The Lounge: The self-hosted web IRC client" loading="lazy" width="1280" height="720" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/11/thelounge-theme-new-morning.png 600w, https://blog.thelazyfox.xyz/content/images/size/w1000/2021/11/thelounge-theme-new-morning.png 1000w, https://blog.thelazyfox.xyz/content/images/2021/11/thelounge-theme-new-morning.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>The Lounge with New Morning theme</figcaption></figure><h2 id="installation">Installation</h2><p>This application is available on <a href="https://github.com/thelounge?ref=blog.thelazyfox.xyz">GitHub</a> and <a href="https://hub.docker.com/r/hurlenko/filebrowser?ref=blog.thelazyfox.xyz">DockerHub</a> and is super straight-forward to install.<br>On my side, I installed it using docker-compose. No worries, you can find well detailed <a href="https://thelounge.chat/docs/install-and-upgrade?ref=blog.thelazyfox.xyz">package</a> or <a href="https://thelounge.chat/docs/install-and-upgrade?ref=blog.thelazyfox.xyz">Docker</a> installation steps in their <a href="https://href.li/?https%3A%2F%2Ffilebrowser.org%2F=&amp;ref=blog.thelazyfox.xyz" rel="nofollow ugc noopener">documentation</a>.</p><p>First thing first, you need to create a folder for this project and put inside your <em>docker-compose.yml</em> file.</p><p>As you know, IRC provide features to get other user hostname or IP address very easily, for that reason, we are going to run it behind a VPN.<br>I will not get into details regarding the VPN part, if you need more details, read the article <a href="https://blog.thelazyfox.xyz/routing-docker-traffic-through-a-vpn-container/">Routing Docker traffic through a VPN container</a>.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/routing-docker-traffic-through-a-vpn-container/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Routing Docker traffic through a VPN container</div><div class="kg-bookmark-description">I&#x2019;m using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="The Lounge: The self-hosted web IRC client"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-ovpn-docker.png" alt="The Lounge: The self-hosted web IRC client"></div></a></figure><p>The docker-compose.yml file is pretty simple to setup, you just need to map the config folder.</p><figure class="kg-card kg-code-card"><pre><code class="language-yaml">version: &quot;3.2&quot;
services:
  vpn:
    container_name: vpn-thelounge
    image: dperson/openvpn-client:latest
    restart: unless-stopped
    networks:
      default:
        ipv4_address: 172.30.0.2
    dns:
      - 9.9.9.9
      - 8.8.8.8
    ports:
      - 9000:9000
    cap_add:
      - NET_ADMIN
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
    security_opt:
      - label:disable
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Paris
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - /home/thelazyfox/docker/thelounge/vpn/config:/vpn
    command: &apos;-f &quot;&quot; -r 172.30.0.2/24&apos;
    healthcheck:
      test: [&quot;CMD&quot;, &quot;curl&quot;, &quot;-Ss&quot;, &quot;ifconfig.co&quot;]
      start_period: 5s
      interval: 60s
      timeout: 10s
      retries: 3

  thelounge:
    container_name: thelounge
    image: thelounge/thelounge:latest
    restart: unless-stopped
    depends_on:
       - vpn
    network_mode: &quot;service:vpn&quot;
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Paris
    volumes:
      - /home/thelazyfox/docker/thelounge/config:/var/opt/thelounge

networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.30.0.0/16
          gateway: 172.30.0.255
</code></pre><figcaption>thelounge/docker-compose.yaml</figcaption></figure><p><em>Please remember to create all folders and files before trying to launch your docker-compose.</em></p><p>How does it look like on my server:</p><pre><code>thelounge/
      |_ docker-compose.yaml
      |_ config/
      |_ vpn/          
          |_ config/ </code></pre><p>And now, you just have to launch your docker-compose file with the following command <code>cd thelounge &amp;&amp; docker-compose up -d</code></p><h2 id="features">Features</h2><p>TheLounge is very easy to use and complete so I will let you discover all the features by yourself.</p><p>We can highlight as main capabilities:</p><ul><li>Push notifications</li><li>File upload</li><li>Always connected</li><li>Responsive interface</li><li>Multi user support<br><em>In case of trouble, their <a href="https://thelounge.chat/docs/users?ref=blog.thelazyfox.xyz">user administration doc</a> is super complete</em></li><li><a href="https://www.npmjs.com/search?q=keywords%3Athelounge-theme&amp;ref=blog.thelazyfox.xyz">Themes management</a><br><em>TheLounge is currently integrated into <a href="https://blog.thelazyfox.xyz/theme-park/">Theme.park</a> if you are looking for custom themes</em></li></ul><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.thelazyfox.xyz/theme-park/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Theme.park: Custom themes for your favorite apps!</div><div class="kg-bookmark-description">Theme.park is a Github project which propose a collection of of themes/skins for most of your favorites apps!</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.thelazyfox.xyz/favicon.png" alt="The Lounge: The self-hosted web IRC client"><span class="kg-bookmark-author">TheLazyFox&apos;s Blog</span><span class="kg-bookmark-publisher">TheLazyFox</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.thelazyfox.xyz/content/images/2021/01/themepark-banner.png" alt="The Lounge: The self-hosted web IRC client"></div></a></figure><p>If you still have some doubts, give a try to the <a href="https://demo.thelounge.chat/?ref=blog.thelazyfox.xyz#/connect">demo client</a>.</p><h2 id="conclusion">Conclusion</h2><p>TheLounge is the best self-hosted IRC solution available from my point of view, easy to setup and configure. It&apos;s a must-have in your setup, go for it!</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Docker linuxserver/rutorrent deprecated, what I have done!]]></title><description><![CDATA[The LinuxServer team has deprecated their image linuxserver/rtorrent/rutorrent due to a lack of maintainers!
I will explain in this article what I have done to replace this image.]]></description><link>https://blog.thelazyfox.xyz/docker-linuxserver-rutorrent-deprecated-what-i-have-done/</link><guid isPermaLink="false">62fa86db1e51e000012f25dc</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Tue, 18 May 2021 06:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/05/publication-cover-rutorrent.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/05/publication-cover-rutorrent.png" alt="Docker linuxserver/rutorrent deprecated, what I have done!"><p>The <a href="https://www.linuxserver.io/?ref=blog.thelazyfox.xyz">LinuxServer</a> team has deprecated their image <strong>linuxserver/rtorrent/rutorrent</strong> due to a lack of maintainers!<br>They now suggest to use <a href="https://hub.docker.com/r/crazymax/rtorrent-rutorrent?ref=blog.thelazyfox.xyz"><strong>crazymax/rtorrent-rutorrent</strong></a> instead. I have decided to give it a try and finally after few tests and adaptations, I have migrated for good!</p><p>I will detail below all customization I have made to keep the same experience and usage I had with the previous one.</p><p>&#x2139;&#xFE0F; It&apos;s important to mention that most of the changes introduced on this image has been made, by the author, to avoid to use rutorrent plugins at the maximum.</p><p>More details below! &#x1F609;</p><blockquote>Please note that this image can be even more customized if you build it yourself</blockquote><h2 id="download-folders">Download folders</h2><p>Linuxserver is using:</p><ul><li>A temp folder &#xA0;<code>/downloads/incoming</code></li><li>A finished folder <code>/downloads/completed</code></li><li>A watch folder <code>/downloads/watch</code> available to be used with <strong>Autotools&gt;AutoWatch</strong></li></ul><p>Crazy-max is using:</p><ul><li>A temp folder <code>/downloads/temp</code></li><li>A finished folder <code>/downloads/complete</code> &#x26A0;&#xFE0F;<em>Note the slight difference here</em></li><li>A watch folder <code>/data/rtorrent/watch</code> which is the default rtorrent folder (Remember the note about getting rid of rutorrent plugins?)</li><li>I have anyway created <code>/data/watched</code> to keep using <strong>Autotools&gt;AutoWatch </strong>easily</li></ul><h2 id="configuration">Configuration </h2><h3 id="environment-file-and-port-management">Environment file and port management</h3><p>A lot of settings can be configured from the environment file, please find below mine and some explanations</p><ul><li>The property <strong>RU_REMOVE_CORE_PLUGINS </strong>gives you the ability to remove completely some rutorrent plugins when you create your container (Remember the note about... ok you got it!). Be careful, some components have cross-dependencies! For example, <strong>Autotools </strong>requires <strong>data </strong>to work properly. </li><li>At the end of the file, you can see 5 properties to customized all the ports. It&apos;s a feature I have requested, thanks for the super-fast implementation. &#xA0;This is a must have if you want to use more than one container behind VPN and avoid conflicts</li><li>I also customized MEMORY_LIMIT and UPLOAD_MAX_SIZE for container with huge volumes of data</li><li>Find out more on <a href="https://github.com/crazy-max/docker-rtorrent-rutorrent?ref=blog.thelazyfox.xyz#readme">GitHub</a></li></ul><figure class="kg-card kg-code-card"><pre><code>TZ=Europe/Paris
PUID=1026
PGID=100

MEMORY_LIMIT=1024M
UPLOAD_MAX_SIZE=32M
OPCACHE_MEM_SIZE=256
MAX_FILE_UPLOADS=10
REAL_IP_FROM=0.0.0.0/32
REAL_IP_HEADER=X-Forwarded-For
LOG_IP_VAR=remote_addr

XMLRPC_AUTHBASIC_STRING=rTorrent XMLRPC restricted access
RUTORRENT_AUTHBASIC_STRING=ruTorrent restricted access
WEBDAV_AUTHBASIC_STRING=WebDAV restricted access

RT_LOG_LEVEL=info
RT_LOG_EXECUTE=false
RT_LOG_XMLRPC=false

RU_REMOVE_CORE_PLUGINS=httprpc,_cloudflare,_noty,_noty2,cookies,extratio,extsearch,feeds,filedrop,history,loginmgr,lookat,retrackers,rss,rssurlrewrite,rutracker_check,scheduler,screenshots,show_peers_like_wtorrent,spectrogram,trafic,unpack,uploadeta,xmpp
RU_HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 6.0; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
RU_HTTP_TIME_OUT=30
RU_HTTP_USE_GZIP=true
RU_RPC_TIME_OUT=5
RU_LOG_RPC_CALLS=false
RU_LOG_RPC_FAULTS=true
RU_PHP_USE_GZIP=false
RU_PHP_GZIP_LEVEL=2
RU_SCHEDULE_RAND=10
RU_LOG_FILE=/data/rutorrent/rutorrent.log
RU_DO_DIAGNOSTIC=true
RU_SAVE_UPLOADED_TORRENTS=true
RU_OVERWRITE_UPLOADED_TORRENTS=false
RU_FORBID_USER_SETTINGS=false
RU_LOCALE=UTF8

RT_DHT_PORT=6840
XMLRPC_PORT=8040
RUTORRENT_PORT=33440
WEBDAV_PORT=9040
RT_INC_PORT=50040</code></pre><figcaption>My environement file</figcaption></figure><h3 id="-rtorrent-rc-customization">.rtorrent.rc customization </h3><p>The .rtorrent.rc configuration file (<code>/data/rtorrent/.rtorrent.rc</code>) contains by default few customization worth to mention.<br><br>First of all, these 2 lines:</p><pre><code># Watch a directory for new torrents, and stop those that have been deleted
schedule2 = watch_directory, 1, 1, (cat,&quot;load.start=&quot;,(cfg.watch),&quot;*.torrent&quot;)
schedule2 = untied_directory, 5, 5, (cat,&quot;stop_untied=&quot;,(cfg.watch),&quot;*.torrent&quot;)</code></pre><p><br>They activate the usage of the rtorrent watch folder (<code>/data/rtorrent/watch</code>) to import the torrent and label them (if inside a folder) to get rid of <strong>Autotools&gt;</strong>A<strong>utowatch </strong>and <strong>Autotools&gt;Autolabel</strong>. I have commented these 2 lines as rtorrent watch feature is not recursive above one folder so I will keep using <strong>Autotools.</strong><br>In addition, it creates also a binding between the torrent file in the watch folder (which is not delete when imported) and its session. Remove one removes the other one and I didn&apos;t want to use it that way.</p><p>Then there is 3 lines to avoid using <strong>Autotools&gt;Automove</strong>. I have commented it as well to keep using <strong>Autotools</strong>.</p><pre><code># Move finished (no need Autotools/Automove plugin on ruTorrent)
method.insert = d.get_finished_dir, simple, &quot;cat=$cfg.download_complete=,$d.custom1=&quot;
method.insert = d.move_to_complete, simple, &quot;d.directory.set=$argument.1=; execute=mkdir,-p,$argument.1=; execute=mv,-u,$argument.0=,$argument.1=; d.save_full_session=&quot;
method.set_key = event.download.finished,move_complete,&quot;d.move_to_complete=$d.data_path=,$d.get_finished_dir=&quot;
directory.default.set = (cat,(cfg.download_complete))</code></pre><p>And last but not least!</p><pre><code># Erase data when torrent deleted (no need erasedata plugin on ruTorrent)
method.set_key = event.download.erased,delete_erased,&quot;execute=rm,-rf,--,$d.data_path=&quot;</code></pre><p>&#x26A0;&#xFE0F;<strong>This is a warning sign to catch your attention as its a topic worth to mention!</strong><br>This configuration is setup to automatically delete all data files associated with a torrent when using the function &quot;<em>Remove</em>&quot; in the interface which normally only remove the torrent and not the data.<br>To remove the torrent and the data, you will normally have to use <em>Remove and Delete data</em> in the interface, which is provided by the plugin <strong>erasedata</strong>. The author of the image put this configuration line by default to avoid having to use erasedata plugin, nevertheless I do not agree with that and I find the ability to remove a torrent without touching its data pretty useful so I also commented this line.</p><blockquote>Using Delete to remove the torrent but not the data is useful for torrent which do not verify at 100% for example (missing a file or whatever)</blockquote><h2 id="migrating-torrents-and-session">Migrating torrents and session</h2><p>After few tests, I was not able to migrate &quot;easily&quot; my session folder between the 2 images. I tried to:</p><ul><li>Copy the session folder content from one image to the other</li><li>Run a search and replace <code>/downloads/completed</code> to <code>/downloads/complete</code></li><li>Start the container</li></ul><p>It never worked as expected! As you know me well, I never would have accepted to migrate manually one by one so I did a python script to ease this by: </p><ul><li>Find all torrents in the watch folder (Do a copy before!)</li><li>Read the associated session files</li><li>Extract the path after /downloads/completed</li><li>Rebuild a tree of folders and move .torrent inside so you can just move it in your watch folder and let the magic happens! &#x2728;</li></ul><p>You will have to run through the content checking for all of them but at least, no manual operations.</p><pre><code class="language-python">import re
import sys,os
import shutil
import urlparse

try:
    os.makedirs(&quot;_done&quot;)
except OSError:
    if not os.path.isdir(&quot;_done&quot;):
        raise

files = [f for f in os.listdir(&apos;.&apos;) if os.path.isfile(f)]

for f in files:
    if f.endswith(&apos;.torrent&apos;):

        print(&quot;Filename:&quot; + f)
        sessionFilename = f +&quot;.rtorrent&quot;
        libFilename = f +&quot;.libtorrent_resume&quot;

        print(&quot;-- Session:&quot; + sessionFilename)
        print(&quot;-- Libresume:&quot; + libFilename)

        # Reading session file to get the current path
        with open (sessionFilename, &apos;rt&apos;) as sessionFile:
            sessionContent = sessionFile.read()

        # Extract path with regex
        results=re.search(r&apos;(?&lt;=:\/downloads\/complete\/)(.*?)(?=7:hashing)&apos;, sessionContent)
        if results is not None:
            pathRaw=results.group(1)
            print(&quot;-- pathRaw:&quot; + pathRaw)
        else:
            pathRaw=None

        if pathRaw:
            # Remove the base path
            pathCleaned=pathRaw.replace(&quot;/downloads/completed/&quot;, &quot;&quot;)

            # Remove trailing slash
            pathTarget=re.sub(&apos;/[^/]+$&apos;, &apos;/&apos;, pathCleaned)

            print(&quot;-- Path:&quot; + pathCleaned)
            print(&quot;-- Target:&quot; + pathTarget)
            print(&quot;-----------------------&quot;)

            # Create the path

            try:
                os.makedirs(pathTarget)
            except OSError:
                if not os.path.isdir(pathTarget):
                    raise

            # Move the files
            path = os.path.join(pathTarget, f)
            shutil.move(f, path)
            shutil.move(sessionFilename, &quot;_done&quot;)
            shutil.move(libFilename, &quot;_done&quot;)</code></pre><p><br>When all of this was done, I had a tought &#x1F4A1;<br>The session incompatibility might be due to the removal of some plugins in the new image, you should try without first and let me know &#x1F609;</p><h2 id="others-features-">Others features!</h2><p>This image contains somes interesting features like the theme importing which let you import new one by just adding a folder into <code>/data/rutorrent/themes</code>. The same works for plugins into <code>/data/rutorrent/plugins</code>.</p><p>You can also overload an existing configuration by adding files into <code>/data/rutorrent/plugins-conf</code>. You can find an example below to active debug and increase the interval of <strong>Autotools </strong>plugin.</p><figure class="kg-card kg-code-card"><pre><code class="language-php">&lt;?php

    // set interval for schedule command in seconds
    $autowatch_interval = 60;

    // set &quot;true&quot; to enable debug output
    $autodebug_enabled = true;</code></pre><figcaption>/data/rutorrent/plugins-conf/autotools.php</figcaption></figure><p>The image also includes a pretty straightforward way to manage the htaccess authentication, more information in the <a href="https://github.com/crazy-max/docker-rtorrent-rutorrent?ref=blog.thelazyfox.xyz#readme">official documentation</a>.</p><h2 id="conclusion"><strong>Conclusion</strong></h2><p>I didn&apos;t want to keep using a deprecetad image and I&apos;ve been seduced by this one and all its possible customizations like the themes or plugins importer/overloader.<br>The easy port customization is awesome as well!</p><p>I hope it helped! Stay tuned!</p>]]></content:encoded></item><item><title><![CDATA[Why is my computer automatically leaving sleep mode after few seconds?]]></title><description><![CDATA[I recently faced a problem with my computer, every time I was setting it in sleep mode it was waking up automatically after few seconds. As I had no explanation at the first sight, I started to dig and I learn few things.]]></description><link>https://blog.thelazyfox.xyz/why-my-computer-is-automatically-leaving-sleep-mode-after-few-seconds/</link><guid isPermaLink="false">62fa86db1e51e000012f25db</guid><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 08 Mar 2021 06:00:00 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1547394765-185e1e68f34e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDN8fGNvbXB1dGVyfGVufDB8fHw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1547394765-185e1e68f34e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MXwxMTc3M3wwfDF8c2VhcmNofDN8fGNvbXB1dGVyfGVufDB8fHw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Why is my computer automatically leaving sleep mode after few seconds?"><p>I recently faced a problem with my computer, every time I was setting it in <a href="https://support.microsoft.com/en-us/windows/shut-down-sleep-or-hibernate-your-pc-2941d165-7d0a-a5e8-c5ad-8c972e8e6eff?ref=blog.thelazyfox.xyz">sleep mode</a> it was waking up automatically after few seconds. As I had no explanation at the first sight, I started to dig and I learn few things.</p><p>First of all, Windows provides the ability to schedule timers to wake your computer. I never used that but just to clear this cause, you can run the following command in your Command Prompt (Win + R &gt; cmd &gt; Enter)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-waketimers.jpg" class="kg-image" alt="Why is my computer automatically leaving sleep mode after few seconds?" loading="lazy" width="670" height="172" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/03/powercfg-waketimers.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-waketimers.jpg 670w"><figcaption>powercfg /waketimers</figcaption></figure><p>It confirms to me there is nothing configured! Next!</p><p>The following command shows which devices are authorized to wake up your computer, like your mouse or keyboard for example. We are all used to hit one of them to wake the computer, right?</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-wakearmed.jpg" class="kg-image" alt="Why is my computer automatically leaving sleep mode after few seconds?" loading="lazy" width="670" height="172" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/03/powercfg-wakearmed.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-wakearmed.jpg 670w"><figcaption>powercfg /devicequery wake_armed</figcaption></figure><p>As my network adapter is part of that list, I was thinking about a Wake-On-Lan behavior triggered from another device on the network but as it&apos;s part of a bigger list, I wanted to be sure and finally found the perfect command for this.</p><p>This command provides information about the last timer or device which woke up your computer. To make it efficient, put your computer in sleep mode, wait for the undesired wake up to happen and then run the command.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-lastwake.jpg" class="kg-image" alt="Why is my computer automatically leaving sleep mode after few seconds?" loading="lazy" width="670" height="172" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/03/powercfg-lastwake.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/03/powercfg-lastwake.jpg 670w"><figcaption>powercfg /lastwake</figcaption></figure><p>It confirms the theory! A device on my network obviously doesn&apos;t want my computer to sleep quietly!</p><p>Now I need to turn that off and there is two options for that:</p><ol><li>Chase the signal and shut it down</li><li>Configure my computer to not take care of that signal</li></ol><p>As I really don&apos;t want any device to be able to wake up my computer, I have looked at the second option first, to kill the problem and it&apos;s pretty easy to do.</p><ol><li>Hit Win + R &gt; type <em>devmgmt.msc &gt; </em>Enter</li><li>Identity the network adapter found with the command above</li><li>Right-click &gt; Properties</li><li>Open &quot;Power Management&quot; tab</li><li>Uncheck &quot;Allow this device to wake the computer&quot;</li><li>Click OK and quit</li></ol><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/devmgmt.msc.jpg" width="396" height="202" loading="lazy" alt="Why is my computer automatically leaving sleep mode after few seconds?"></div><div class="kg-gallery-image"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/devmgmt.msc-ethernet-1.jpg" width="778" height="569" loading="lazy" alt="Why is my computer automatically leaving sleep mode after few seconds?" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/03/devmgmt.msc-ethernet-1.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/03/devmgmt.msc-ethernet-1.jpg 778w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.thelazyfox.xyz/content/images/2021/03/devmgmt.msc-ethernet-wake-off.jpg" width="397" height="469" loading="lazy" alt="Why is my computer automatically leaving sleep mode after few seconds?"></div></div></div><figcaption>Steps 1 to 6</figcaption></figure><p>Now my computer can finally sleep quietly (and so do I) without any savage packages waking it up!</p><p>Now I&apos;m still wondering what was causing this issue. I have tried some Wake On LAN package sniffers like <a href="https://www.depicus.com/wake-on-lan/wake-on-lan-monitor?ref=blog.thelazyfox.xyz">Wake On LAN Monitor</a> but so far I didn&apos;t find anything. <br>May be because the packages are only sent when my computer is off?</p><p>If you have any suggestion, hit the comment section!</p><p></p>]]></content:encoded></item><item><title><![CDATA[Routing Docker traffic through a VPN container]]></title><description><![CDATA[I'm using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.]]></description><link>https://blog.thelazyfox.xyz/routing-docker-traffic-through-a-vpn-container/</link><guid isPermaLink="false">62fa86db1e51e000012f25da</guid><category><![CDATA[Docker]]></category><category><![CDATA[VPN]]></category><category><![CDATA[Synology]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 15 Feb 2021 07:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-ovpn-docker.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-ovpn-docker.png" alt="Routing Docker traffic through a VPN container"><p>I&apos;m using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.</p><p>I found out a <a href="https://github.com/dperson/openvpn-client?ref=blog.thelazyfox.xyz">docker container</a> which suits perfectly to what I wanted to do and I will show you how to quickly built this setup.</p><h2 id="prerequisites">Prerequisites</h2><h3 id="folder-structure">Folder structure</h3><p>As a first step, you have to create a folder structure as the following one.</p><pre><code>docker-vpn-project/
      |_ docker-compose.yaml 
      |_ vpn
          |_ config
      |_ my-container # If needed
          |_ config</code></pre><h3 id="openvpn-files">OpenVPN files</h3><p>To be able to work, this container will need some OpenVPN configurations from my current VPN provider, Windscribe.</p><p>On their website, you can easily find an <a href="https://windscribe.com/getconfig/openvpn?ref=blog.thelazyfox.xyz">OpenVPN configuration generator</a> which provides you the key elements to continue:</p><ul><li>An *.ovpn configuration file</li><li>A certificate and a private key</li><li>An username/password for the authentication </li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-1.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="804" height="316" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/02/image-1.png 600w, https://blog.thelazyfox.xyz/content/images/2021/02/image-1.png 804w" sizes="(min-width: 720px) 720px"><figcaption>Windscribe OpenVPN Generator</figcaption></figure><p>You have now downloaded and extracted all the files and it looks like this:</p><figure class="kg-card kg-image-card"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-2.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="381" height="73"></figure><p>Now it&apos;s time to copy these files into your VPN configuration folder. </p><h3 id="authentication-file">Authentication file</h3><p>You will need to create a file &quot;vpn.auth&quot; for the credentials. This file must contains only 2 lines of text, the first one is your username and the second line is the password, both provided by your VPN provider.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-3.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="160" height="82"><figcaption>vpn.auth file</figcaption></figure><p>Now you need to open the *.ovpn file to target the authentication file we have create. You just need to add the path of the file inside the container <code>/vpn/vpn.auth</code> following the existing auth-user-pass section (line 7).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-4.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="291" height="298"><figcaption>*.ovpn configuration with the path to the vpn.auth file</figcaption></figure><p>How does it look on my Synology NAS:</p><pre><code>docker-vpn-project/
      |_ docker-compose.yaml 
      |_ vpn
          |_ config
                |_ ca.crt  
                |_ ta.key
                |_ vpn.auth
                |_ Windscribe-Zurich-Alphorn-GCM.ovpn
      |_ my-container # If needed
          |_ config</code></pre><h2 id="docker-compose-yaml">Docker-compose.yaml</h2><p>Now let&apos;s deep-dive into the docker-compose.yaml file. For this example, I &#xA0;route through VPN a librespeed container but you can easily use this also for Sonarr, Radarr, ruTorrent, qbitTorrent, Jackett, Prowlarr, etc.</p><pre><code class="language-yaml">version: &quot;2.3&quot; 
services: 
  vpn:
    container_name: my-vpn
    image: dperson/openvpn-client:latest
    restart: unless-stopped
    networks:
      default:
        ipv4_address: 172.51.0.2
    dns:
      - 9.9.9.9
    ports:
      - 1234:80
    cap_add:
      - NET_ADMIN
    sysctls:
      - net.ipv6.conf.all.disable_ipv6=0
    security_opt:
      - label:disable
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Zurich
    devices:
      - /dev/net/tun:/dev/net/tun
    volumes:
      - ./vpn/config:/vpn
    command: &apos;-f &quot;&quot; -r 192.168.0.0/24&apos;
    healthcheck:
      test: [&quot;CMD-SHELL&quot;, &quot;if [ $$(curl --silent https://ifconfig.co/country-iso) = &apos;DE&apos; ]; then exit 0; else exit 1; fi&quot;]
      start_period: 5s
      interval: 5s
      timeout: 10s
      retries: 3

  my-container:
    image: linuxserver/librespeed:version-5.2.4
    container_name: my-container
    restart: unless-stopped
    network_mode: &quot;service:vpn&quot;
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Zurich
 
networks:
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.51.0.0/16
          gateway: 172.51.0.255</code></pre><p>The key section are the following:</p><ul><li><strong><em>vpn.networks </em></strong>- Used to define a fixed IP address to your VPN container into the current network range</li><li><strong><em>vpn.dns </em></strong>- The DNS address your VPN container is going to use to resolve domains. I&apos;m using <a href="https://www.quad9.net/?ref=blog.thelazyfox.xyz">Quad9</a></li><li><strong><em>vpn.dns </em></strong>- The ports you need to map. As all the traffic is redirected through the VPN, you need to map all the ports into the VPN container and not your services container</li><li><strong><em>my-container.network_mode </em></strong>- No custom port declaration, we are delegating the complete network management to the VPN service, running inside the same docker-compose.yaml file, named vpn.<br><em>Note: If you want to run the VPN container in a separate docker-compose.yaml or docker run command, you will need to set <code>container:vpn</code> instead of <code>service:vpn</code></em></li><li><strong><em>networks </em></strong>- We are defining the default network of this stack</li><li><strong>healthcheck </strong>- This healthcheck command is going to define my VPN container as unhealthy if its connection is not located in Germany</li></ul><p>And now you are ready to test!</p><h2 id="container-creation-and-connectivity">Container creation and connectivity</h2><p>You can now create and start your Docker stack with the command:<br><code>sudo docker-compose up -d</code></p><p>You can check your VPN container logs to check if everything is fine. It should look like this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-6.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="831" height="392" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/02/image-6.png 600w, https://blog.thelazyfox.xyz/content/images/2021/02/image-6.png 831w" sizes="(min-width: 720px) 720px"><figcaption>VPN container started without issues</figcaption></figure><p>As soon as both of the containers are created and started, it&apos;s time to check your connectivity and especially your IP address.</p><p>Let&apos;s start with the VPN container, you can run the following command:<br> <code>sudo docker exec -it my-vpn curl ifconfig.co/json</code></p><p>You can see below that your IP address is now located in Zurich, the VPN is correctly connected!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image-5.png" class="kg-image" alt="Routing Docker traffic through a VPN container" loading="lazy" width="234" height="264"><figcaption>VPN Container connectivity test</figcaption></figure><p>Now, we are going to check the librespeed container, you can run the following command:<br><code>sudo docker exec -it my-container curl ifconfig.co/json</code></p><p>The result is exactly the same! Congratulations, you are now routing all your Librespeed container traffic through a VPN container! &#x1F389;</p><h2 id="debug">Debug</h2><p>If you encounter any issue related to the <code>/dev/net/tun</code> device interface, as I did on my Synology NAS, you might need to create it before.<br>I made a small bash file you can easily run to fix this issue!</p><ul><li>Create the file <br><code>sudo nano tun.sh</code></li><li>Paste this content inside</li></ul><pre><code class="language-bash">#!/bin/bash

# Create the necessary file structure for /dev/net/tun
if ( [ ! -c /dev/net/tun ] ); then
    if ( [ ! -d /dev/net ] ); then
        mkdir -m 755 /dev/net
    fi
     mknod /dev/net/tun c 10 200
fi

# Load the tun module if not already loaded
if ( !(lsmod | grep -q &quot;^tun\s&quot;) ); then
    insmod /lib/modules/tun.ko
fi

 # Load iptables mangle is not already loaded
if ( !(lsmod |grep -q &quot;^iptable_mangle\s&quot;) ); then
    insmod /lib/modules/iptable_mangle.ko
fi
</code></pre><ul><li>Make it executable<br><code>sudo chmod +x tun.sh</code></li><li>Execute it<br><code>sudo ./tun.sh</code></li></ul>]]></content:encoded></item><item><title><![CDATA[Install Wireguard on your Synology NAS]]></title><description><![CDATA[WireGuard is a new, modern and open-source VPN protocol. You might have heard about it as it has been merge a exactly a year ago in the Linux core. ]]></description><link>https://blog.thelazyfox.xyz/install-wireguard-on-your-synology-nas/</link><guid isPermaLink="false">62fa86db1e51e000012f25d9</guid><category><![CDATA[VPN]]></category><category><![CDATA[Synology]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 08 Feb 2021 07:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-wireguard-synology.png" medium="image"/><content:encoded><![CDATA[<h2 id="what-is-wireguard">What is Wireguard?</h2><blockquote>WireGuard is a free and open-source software application and communication protocol that implements virtual private network techniques to create secure point-to-point connections in routed or bridged configurations</blockquote><img src="https://blog.thelazyfox.xyz/content/images/2021/11/publication-cover-wireguard-synology.png" alt="Install Wireguard on your Synology NAS"><p><a href="https://en.wikipedia.org/wiki/WireGuard?ref=blog.thelazyfox.xyz">WireGuard</a> is a new, modern and open-source VPN protocol. You might have heard about it as <a href="https://lists.zx2c4.com/pipermail/wireguard/2020-January/004906.html?ref=blog.thelazyfox.xyz">it has been merge a exactly a year ago in the Linux core</a>. </p><h2 id="is-my-vpn-provider-compatible">Is my VPN Provider compatible?</h2><p>Some of the most famous providers already offer WireGuard through their mobile or desktop applications and some of them also provide configuration files for custom install!</p><p><strong>WindScribe: </strong><a href="https://href.li/?https%3A%2F%2Fblog.windscribe.com%2Fintroducing-wireguard-76a1670700a6=&amp;ref=blog.thelazyfox.xyz" rel="nofollow ugc noopener">https://blog.windscribe.com/introducing-wireguard-76a1670700a6</a><br><strong>NordVPN: </strong><a href="https://href.li/?https%3A%2F%2Fnordvpn.com%2Ffr%2Fblog%2Fnordlynx-protocol-wireguard=&amp;ref=blog.thelazyfox.xyz" rel="nofollow ugc noopener">https://nordvpn.com/fr/blog/nordlynx-protocol-wireguard</a><br><strong>ProtonVPN: </strong><a href="https://href.li/?https%3A%2F%2Fprotonvpn.com%2Fblog%2Fwireguard-donation%2F=&amp;ref=blog.thelazyfox.xyz" rel="nofollow ugc noopener">https://protonvpn.com/blog/wireguard-donation</a></p><h2 id="how-to-use-it-on-my-synology-nas">How to use it on my Synology NAS?</h2><p>I have recently discovered a <a href="https://github.com/runfalk/synology-wireguard?ref=blog.thelazyfox.xyz">GitHub project</a> which let you build your own custom Synology package, fully compatible with your Synology NAS to let you use the <em>&quot;wg&quot; </em>and <em>&quot;wg-quick&quot;</em> commands of WireGuard.</p><p>The first step is to identity the CPU architecture your Synology NAS is running and to ensure your Kernel is above 3.10.</p><p>You can easily check all of that by searching for your Synology NAS model in the official documentation: <a href="https://www.synology.com/en-global/knowledgebase/DSM/tutorial/Compatibility_Peripherals/What_kind_of_CPU_does_my_NAS_have?ref=blog.thelazyfox.xyz">What kind of CPU does my Synology NAS have?</a>.</p><p>As soon as you have identity it, you can either download a pre-built version in the <a href="https://href.li/?https%3A%2F%2Fgithub.com%2Frunfalk%2Fsynology-wireguard%2Freleases=&amp;ref=blog.thelazyfox.xyz">GitHub releases</a> section or you can built it yourself. </p><h3 id="pre-built-versions">Pre-built versions</h3><p>It&apos;s pretty straight forward, you just have to download the .spk file matching your Synology NAS&apos; CPU architecture.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/wireguard-github-releases.jpg" class="kg-image" alt="Install Wireguard on your Synology NAS" loading="lazy" width="656" height="399" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/02/wireguard-github-releases.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/02/wireguard-github-releases.jpg 656w"><figcaption>WireGuard for Synology v1.0.20200729 releases</figcaption></figure><h3 id="manual-build">Manual build</h3><p>To build manually your package, you will have to run the following commands:</p><ol><li>Connect to your Synology NAS via SSH and use a folder dedicated for that purpose<br><code>cd /volume1/testing</code></li><li>Download the source code from GitHub<br><code>git clone https://github.com/runfalk/synology-wireguard.git</code></li><li>Browse the source code&apos;s folder freshly cloned<br><code>cd synology-wireguard/</code></li><li>Start the building process<br><code>docker build -t synobuild .</code></li><li>Generate the package matching your Synology NAS&apos; CPU architecture. On my side, it&apos;s &quot;braswell&quot; with DSM 6.2<br><code>docker run --rm --privileged --network=host --env PACKAGE_ARCH=braswell --env DSM_VER=6.2 -v $(pwd):/result_spk synobuild</code></li><li>You can find the .spk file in the WireGuard. Version number will change so use the tab key.<br><code>cd WireGuard-0.0.20210124 &amp;&amp; ls *.spk</code></li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/wireguard-build-result.jpg" class="kg-image" alt="Install Wireguard on your Synology NAS" loading="lazy" width="349" height="112"><figcaption>Build result: Success!</figcaption></figure><h3 id="package-installation">Package installation</h3><p>You can now easily install the package with this command line or through the DSM interface: <code>synopkg install WireGuard-braswell-1.0.20210124.spk</code></p><p>To activate the package, just run <code>synoservice --start pkgctl-WireGuard</code>.<br>Use <code>synoservice --status pkgctl-WireGuard</code> to check if the package is running correctly.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/image.png" class="kg-image" alt="Install Wireguard on your Synology NAS" loading="lazy" width="883" height="134" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/02/image.png 600w, https://blog.thelazyfox.xyz/content/images/2021/02/image.png 883w" sizes="(min-width: 720px) 720px"><figcaption>Package installation via cli</figcaption></figure><h3 id="use-wireguard">Use WireGuard</h3><p>You can now use <em>&quot;wg&quot;</em> and <em>&quot;wg-quick&quot;</em> commands of WireGuard directly on your Synology NAS.</p><figure class="kg-card kg-image-card"><img src="https://blog.thelazyfox.xyz/content/images/2021/02/wireguard-cli-help.jpg" class="kg-image" alt="Install Wireguard on your Synology NAS" loading="lazy" width="610" height="183" srcset="https://blog.thelazyfox.xyz/content/images/size/w600/2021/02/wireguard-cli-help.jpg 600w, https://blog.thelazyfox.xyz/content/images/2021/02/wireguard-cli-help.jpg 610w"></figure><p> <br>Feel free to share your projets with WireGuard in the comments. &#x1F680; </p>]]></content:encoded></item><item><title><![CDATA[Generate a 
Let's Encrypt wildcard certificate on Synology with Docker and Cloudflare]]></title><description><![CDATA[Following my setup of AdGuard Home, I found out it can manage DNS-over-HTTPS and DNS-over-TLS but it needs valid SSL certificates for that purpose.]]></description><link>https://blog.thelazyfox.xyz/generate-a-lets-encrypt-wildcard-certificate-on-synology-with-docker-and-cloudflare/</link><guid isPermaLink="false">62fa86db1e51e000012f25d7</guid><category><![CDATA[Docker]]></category><category><![CDATA[Self-hosted]]></category><category><![CDATA[Synology]]></category><category><![CDATA[SSL]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[TheLazyFox]]></dc:creator><pubDate>Mon, 01 Feb 2021 22:00:00 GMT</pubDate><media:content url="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-le-synology-cloudflare.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.thelazyfox.xyz/content/images/2021/02/publication-cover-le-synology-cloudflare.png" alt="Generate a 
Let&apos;s Encrypt wildcard certificate on Synology with Docker and Cloudflare"><p>Following my setup of <a href="https://blog.thelazyfox.xyz/adguard-home-a-world-without-ads-and-trackers/">AdGuard Home</a>, I found out it can manage DNS-over-HTTPS and DNS-over-TLS but it needs valid SSL certificates for that purpose.</p><p>In addition, I was looking for a solution to generate easily a wildcard certificate to manage all subdomains applications I&apos;m hosting on my Synology NAS without having to regenerate independantly all certificates everytime I launch a new subdomain.</p><p>I will explain to you how to generate quickly a valid SSL certificate for your Synology NAS self-hosted subdomains which can also be used in <a href="https://blog.thelazyfox.xyz/adguard-home-a-world-without-ads-and-trackers/">AdGuard Home</a>.</p><blockquote>Why didn&apos;t you used the Synology DSM built-in let&apos;s encrypt generator?</blockquote><p>You might know that Synology offers an interface to generate the certificate with Let&apos;s Encrypt for you natively, nevertheless it doesn&apos;t support yet the wildcard, this is why I used an alternative way to do it.</p><h2 id="setting-up-your-docker-compose-file">Setting up your docker compose file</h2><pre><code class="language-yaml">version: &quot;2.1&quot;
services:
  letsencrypt:
    image: linuxserver/letsencrypt
    container_name: letsencrypt
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/Paris
      - URL=yourdomain.tld
      - SUBDOMAINS=wildcard
      - VALIDATION=dns
      - DNSPLUGIN=cloudflare
      - EMAIL=youremailaddress@protonmail.com
      - DHLEVEL=2048
    volumes:
      - /volume1/docker/letsencrypt:/config</code></pre><p>Few explanations regarding this docker compose:</p><ul><li>URL is your domain</li><li>SUBDOMAINS=wildcard which means it will work for *.yourdomain.tld</li><li>VALIDATION=dns as it&apos;s the only validation method authorized to generate wildcard certificates</li><li>DNSPLUGIN=cloudflare as I&apos;m using Cloudflare </li><li>EMAIL is the email you associate to your certificate, it&apos;s mandatory.</li></ul><p>If you need more detail, you can check on <a href="https://href.li/?https%3A%2F%2Fgithub.com%2Flinuxserver%2Fdocker-letsencrypt=&amp;ref=blog.thelazyfox.xyz" rel="nofollow ugc noopener"><em>GitHub</em></a><em>.</em> <br><br>How does it look on my Synology NAS:</p><pre><code>letsencrypt/
      |_ docker-compose.yaml
      |_ config/         </code></pre><p>And now, you just have to launch your docker-compose file with the following command <code>cd letsencrypt &amp;&amp; docker-compose up -d</code><br><br>You will see the config folder being populated with the following folders</p><ul><li>crontabs</li><li>dns-conf</li><li>etc</li><li>fail2ban</li><li>keys</li><li>logs</li><li>nginx</li><li>php</li><li>www</li><li>...</li></ul><p>Please monitor the logs with <code>sudo docker logs letsencrypt</code> to ensure the container startup phase is totally complete as it can takes time the first time.</p><p>As soon as the startup is completed, browse your volume and open <em>dns-conf/cloudflare.ini. </em>You need to put in that file, your Cloudflare account email address and your Cloudflare account Global API Key so the container can manage by himself the DNS challenge to prove you are the domain owner.<br>You need to fill the file like this:</p><pre><code>dns_cloudflare_email = youremailaddress@protonmail.com
dns_cloudflare_api_key = yourglobalapikey</code></pre><p>You can now save the file and restart the container with this command <code>sudo docker-compose restart</code> and you can check your logs again, everything should have finished smoothly. If there is any issues, it will propose you some solutions.</p><pre><code>2048 bit DH parameters present
SUBDOMAINS entered, processing
Wildcard cert for yourdomain.tld will be requested
E-mail address entered: youremailaddress@protonmail.com
dns validation via cloudflare plugin is selected
Certificate exists; parameters unchanged; starting nginx
Starting 2019/12/30, GeoIP2 databases require personal license key to download. Please manually download/update the GeoIP2 db and save as /config/geoip2db/GeoLite2-City.mmdb
[cont-init.d] 50-config: exited 0.
[cont-init.d] 60-renew: executing...
The cert does not expire within the next day. Letting the cron script handle the renewal attempts overnight (2:08am).
[cont-init.d] 60-renew: exited 0.
[cont-init.d] 99-custom-files: executing...
[custom-init] no custom files found exiting...
[cont-init.d] 99-custom-files: exited 0.
[cont-init.d] done.
[services.d] starting services
[services.d] done.</code></pre><p>Congratulations, you have generated your wildcard certificate for your domain with Let&apos;s Encrypt and Cloudflare! &#x1F389;</p><p>You can now get it by browsing your Let&apos;s Encrypt files in <code>/etc/letsencrypt/live/yourdomain.tld</code> . If it doesn&apos;t work, all certificates related files are also saved in <em><code>/etc/letsencrypt/archive/yourdomain.tld</code></em><br><br>It&apos;s now time to use it! </p><p>If you want to use this certificate in your Synology NAS, you will need <em>privkey1.pem </em>and <em>fullchain1.pem</em>.<br>Just follow these few steps:</p><ol><li>Login to your Synology NAS DSM interface</li><li>Open &quot;Control Panel&quot;</li><li>Open &quot;Security&quot;</li><li>Select the tab &quot;Certificate&quot;</li><li>Click on &quot;Add&quot; &gt; &quot;Add a new certificate&quot; &gt; &quot;Next&quot;</li><li>Give it a name and select &quot;Import certificate&quot; &gt; &quot;Next&quot;</li><li>Set your <em>privkey1.pem</em> in &quot;Private Key&quot;</li><li>Set your <em>fullchain1.pem</em> in &quot;Certificate&quot;</li><li>Finish with &quot;OK&quot; </li></ol><figure class="kg-card kg-image-card"><img src="https://blog.thelazyfox.xyz/content/images/2021/01/synology-certificate.png" class="kg-image" alt="Generate a 
Let&apos;s Encrypt wildcard certificate on Synology with Docker and Cloudflare" loading="lazy" width="467" height="397"></figure><p>Your certificate is now available and can be assigned to your website! &#x1F389;</p><p>Now let&apos;s set it up in <a href="https://blog.thelazyfox.xyz/adguard-home-a-world-without-ads-and-trackers/">AdGuard Home</a>!</p><ol><li>Login to your AdGuard Home interface</li><li>Open &quot;Settings&quot; &gt; &quot;Encryption settings&quot;</li><li>Scroll to the section &quot;Certificates&quot; and paste your <em>fullchain1.pem</em> file content inside</li><li>Scroll to the section &quot;Private Key&quot; and paste your <em>privkey1.pem</em> file content inside</li><li>Finish with &quot;Save config&quot; </li></ol><p>Congratulations, you have setup your wildcard certificate in AdGuard Home, you can now activate DNS-over-HTTPS and DNS-over-TLS! &#x1F389;</p>]]></content:encoded></item></channel></rss>