Uli Köhler

How to configure SMTP server on MikroTik RouterOS

Use the following command in order to configure SMTP settings for a MikroTik router:

/tool e-mail set address=smtp.mydomain.com from="MikroTik <[email protected]>" tls=starttls [email protected] password=uFoome0Noh

Alternatively, you can configure these settings directly on the web interface at WebFig => Tools => EMail.

Posted by Uli Köhler in Allgemein

How to setup Cloudflare DNS-over-HTTPS (DoH) cache on MikroTik RouterOS router

Compared to standard UDP DNS, DNS-over-HTTPS (DoH) provides the huge advantage that – due to it being encrypted, someone able to sniff the traffic will not be able to determine what domain names are being used.

However, consider the disadvantage that the latency of resolving a domain name is significantly larger with DoH – however, setting up the MikroTik router as DNS cache will significantly reduce the overall DNS latency, at least for cached domain names.

The following list of RouterOS commands will setup the internal DNS server as a DNS cache running on DNS-over-HTTPS.

First, download CA certificates onto the router in order to be able to verify CloudFlare’s HTTPS certificates:

/tool fetch url=https://curl.se/ca/cacert.pem

Wait for it to finish downloading, e.g.

[admin@MikroTik] > /tool fetch url=https://curl.se/ca/cacert.pem
      status: finished
  downloaded: 210KiBz pause]
       total: 210KiB
    duration: 1s

Now import the file and setup the DNS server:

/certificate import file-name=cacert.pem passphrase=""
/ip dns set allow-remote-requests=yes cache-size=8192KiB max-concurrent-queries=1000 max-concurrent-tcp-sessions=2000 servers=1.1.1.1 use-doh-server=https://cloudflare-dns.com/dns-query verify-doh-cert=yes

 

Posted by Uli Köhler in MikroTik, Networking

MikroTik webinterface reverse proxy using Traefik

The following Traefik .toml file which reverse proxies a MikroTik router’s WebFig webinterface is based on our Traefik setup from Simple Traefik docker-compose setup with Lets Encrypt Cloudflare DNS-01 & TLS-ALPN-01 & HTTP-01 challenges. It assumes that the MikroTik router is reachable at 10.1.2.3 via HTTP.

No Authentication beyond the MikroTik router’s WebFig internal authentication is performed. However – at least when using our Traefik config from our previous post it enforces HTTPS i.e. encrypted access.

Save the following file under config/mikrotik01.toml. Traefik will automatically reload, no restart will be required.

[http.routers.mikrotik01]
rule = "Host(`mikrotik01.mydomain.com`)"
service = "mikrotik01"

[http.routers.mikrotik01.tls]
certresolver = "cloudflare"

[[http.routers.mikrotik01.tls.domains]]
main = "mydomain.com"
sans = ["*.mydomain.com"]

[http.services]
[http.services.mikrotik01.loadBalancer]
[[http.services.mikrotik01.loadBalancer.servers]]
url = "http://10.1.2.3.4/"

 

Posted by Uli Köhler in MikroTik, Networking, Traefik

XenOrchestra docker-compose setup with Traefik labels

Based on Simple XenOrchestra setup using docker-compose, this extension of our config from that post features Traefik container labels. For the Traefik configuration, see for example our previous post Simple Traefik docker-compose setup with Lets Encrypt Cloudflare DNS-01 & TLS-ALPN-01 & HTTP-01 challenges

This setup uses a Wildcard certificate but you can also use a non-wildcard cert (e.g. if you don’t have access to the DNS for the DNS01 challenge) by just deleting both traefik.http.routers.xenorchestra.tls.domains... lines and selecting a suitable resolver.

version: '3'
services:
    xen-orchestra:
        restart: unless-stopped
        image: ronivay/xen-orchestra:latest
        container_name: xen-orchestra
        network_mode: host
        stop_grace_period: 1m
        environment:
            - HTTP_PORT=1780
        cap_add:
          - SYS_ADMIN
        security_opt:
          - apparmor:unconfined
        volumes:
          - ./xo-data:/var/lib/xo-server
          - ./redis-data:/var/lib/redis
        labels:
          - "traefik.enable=true"
          - "traefik.http.routers.xenorchestra.rule=Host(`xenorchestra.mydomain.com`)"
          - "traefik.http.routers.xenorchestra.entrypoints=websecure"
          - "traefik.http.routers.xenorchestra.tls.certresolver=cloudflare"
          - "traefik.http.routers.xenorchestra.tls.domains[0].main=mydomain.com"
          - "traefik.http.routers.xenorchestra.tls.domains[0].sans=*.mydomain.com"
          - "traefik.http.services.xenorchestra.loadbalancer.server.port=1780"

You can now login with the default credentials: [email protected] and password admin

Posted by Uli Köhler in Networking, Virtualization

nginx FritzBox webinterface reverse proxy

The following nginx config allows remote access to a local FritzBox over VPN etc. You explicitly need to set the Host header to fritz.box in the proxied request – else, the FritzBox will reject the request as part of its rebind protection.

server {
        listen 80 default_server;

        access_log off;
        error_log  off;
        location / {
            proxy_pass http://192.168.241.1;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host 'fritz.box';
        }
}

On most linux distributions such as Debian or Ubuntu, install nginx using sudo apt -y install nginx or similar and place our config file as /etc/nginx/sites-enabled/default.

Posted by Uli Köhler in Networking, nginx

How to configure Angular ‘ng serve’ API proxy

In order to proxy /api to http://localhost:62232 for example, first create proxy.conf.json in the same directory where package.json is located:

{
    "/api":
    {
        "target": "http://localhost:62232",
        "secure": false
    }
}

Now we need to modify package.json. Locate the line where ng serve is called, such as:

"start": "ng serve",

and add --proxy-config proxy.conf.json to the arguments of ng serve:

"start": "ng serve --proxy-config proxy.conf.json",

Full example for the scripts section of package.json:

"scripts": {
  "ng": "ng",
  "start": "ng serve --proxy-config proxy.conf.json",
  "build": "ng build --configuration=production",
  "watch": "ng build --watch --configuration development",
  "test": "ng test"
},

 

Posted by Uli Köhler in Angular, Javascript

How to export GPG public key to file using the command line

gpg --armor -o MyKey.gpg --export [Key ID or fingerprint]

For example, with fingerprint

gpg --armor -o MyKey.gpg --export AA15942077B73AE65E88FB4BCFC41606DD8C212E

with (short) key ID:

gpg --armor -o MyKey.gpg --export DD8C212E
Posted by Uli Köhler in Cryptography

How to convert collections.Counter to pandas DataFrame

Pandas can take care of the conversion of a Counter to a DataFrameby itself but you need to add a column label:

pd.DataFrame({"YourColumnLabelGoesHere": counterObject})

Full example

import pandas as pd
from collections import Counter

ctr = Counter()
ctr["a"] += 1
ctr["b"] += 1
ctr["a"] += 1
ctr["a"] += 1
ctr["b"] += 1
ctr["a"] += 1
ctr["c"] += 1

pd.DataFrame({"ctr": ctr})

This will result in the following DataFrame:

 

Posted by Uli Köhler in pandas, Python

structlog minimal example

import structlog

logger = structlog.get_logger()

# Usage example
logger.info("Test log")

 

Posted by Uli Köhler in Python

How to fix WordPress docker image upload size 2M limit

Problem:

You are running your WordPress instance using the official WordPress Apache image.

However, the WordPress Media page has a maximum upload size of 2 Megabytes.

Solution:

This setting is configured in the php.ini used by the WordPress docker image internally. While it is possible to use a custom php.ini, it’s much easier to edit .htaccess . Just edit .htaccess in the wordpress directory where wp-config.php is located and append this after # END WordPress to set the upload limit to 256 Megabytes:

php_value upload_max_filesize 256M
php_value post_max_size 256M
php_value max_execution_time 300
php_value max_input_time 300

The change should be effective immediately after reloading the page. Note that you still might need to configure your reverse proxy (if any) to allow larger uploads. My recommendation is to just try it out as is and if large uploads fail, it’s likely that your reverse proxy is at fault.

Full .htaccess example:

# BEGIN WordPress
# Die Anweisungen (Zeilen) zwischen „BEGIN WordPress“ und „END WordPress“ sind
# dynamisch generiert und sollten nur über WordPress-Filter geändert werden.
# Alle Änderungen an den Anweisungen zwischen diesen Markierungen werden überschrieben.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteRule ^en/wp-login.php /wp-login.php [QSA,L]
RewriteRule ^de/wp-login.php /wp-login.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

php_value upload_max_filesize 256M
php_value post_max_size 256M
php_value max_execution_time 300
php_value max_input_time 300

 

Posted by Uli Köhler in Container, Docker, Wordpress

How to permanently enable IPv4 forwarding in Alpine Linux

This simple command will permanently enable IPv4 forwarding on Alpine Linux. Run this as root:

echo net.ipv4.ip_forward=1 | tee -a /etc/sysctl.conf && sysctl -p

 

Posted by Uli Köhler in Alpine Linux, Networking

How to install tailscale on XCP-NG host

By installing tailscale on XCP-NG hosts, you can provide easier access to your virtualization host using VPN.

Run the following commands via SSH as root on the XCP-NG host:

sudo yum-config-manager --add-repo https://pkgs.tailscale.com/stable/centos/7/tailscale.repo
sudo yum -y install tailscale

and enable & start the tailscale daemon tailscaled:

systemctl enable --now tailscaled

 

Posted by Uli Köhler in Headscale, Networking, Virtualization, VPN

How to install tailscale in Alpine Linux

My recommendation is to just use the community repository:

echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories
apk add -U tailscale

Now you need to add tailscaled to the autostart list and then start the service so you can use it right now:

rc-update add tailscale
/etc/init.d/tailscale start

 

Posted by Uli Köhler in Alpine Linux

How to install usermod in Alpine Linux

Problem:

When trying to use usermod in Alpine Linux, you see the following error message:

-ash: usermod: not found

Solution:

Install usermod and related tools by adding the community repositories and using

echo http://dl-2.alpinelinux.org/alpine/edge/community/ >> /etc/apk/repositories
apk add -U shadow

 

Posted by Uli Köhler in Alpine Linux

How to fix matplotlib OSError: ‘xkcd’ not found in the style library

Problem:

While trying to enable the matplotlib xkcd style using

plt.style.use("xkcd")

you see the following error message:

OSError: 'xkcd' not found in the style library and input is not a valid URL or path; see `style.available` for list of available styles

Solution:

You can’t enable xkcd-style plots by running plt.style.use("xkcd"). Instead, use with plt.xkcd():

with plt.xkcd():
    # TODO your plotting code goes here!
    # plt.plot(x, y) # Example

 

Posted by Uli Köhler in Python

How to set X-Forwarded-Proto header in nginx

Directly after any proxy_pass line add

proxy_set_header X-Forwarded-Proto $scheme;

Typically X-Forwarded-Proto is used together with X-Forwarded-Host like this:

proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

 

Posted by Uli Köhler in Networking, nginx, Wordpress

How to run iperf3 on Synology NAS using SSH

I prefer this method to the GUI docker method because:

  • It’s much more reproducible in practice
  • It involves fewer steps
  • It uses --net=host and therefore doesn’t involve additional routing, bridging or forwarding of packets which might impact performance

Login to the Synology NAS over SSH using a user with admin privileges, then sudo su.

For using iperf3 as a serve, use

docker run  -it --rm --name=iperf3-server --net=host networkstatic/iperf3 -s

For using iperf3 as a client, use

docker run  -it --rm --name=iperf3-client --net=host networkstatic/iperf3 -c 10.1.2.3

 

Posted by Uli Köhler in Networking

Real-world Tailscale iperf3 results between a VM and a bare metal Desktop on a switched network

We tested iperf3 performance using our network based on the following devices:

  • Desktop: Ubuntu 21.10 i7-6700 CPU @ 3.40 GHz, connected using 1Gbase-T to
  • Desktop switch: Mikrotik CSS610-8G-2S+IN connected using 10GBase-T multimode SFP+ module to:
  • Core switch: Mikrotik CRS309-1G-8S+IN, connected using 10GBase-T DAC cable to
  • Virtualization server: i5-6500 CPU @ 3.20GHz running XCP-NG 8.2.1
  • Virtual Machine: Ubuntu 20.04, 4 cores, 8GB RAM

Tailscale version was

1.24.1
  tailscale commit: 1a9302b1edba91d0f638e775faeaa0ce2a6a25f8
  other commit: 1331ed5836e1a0ab32b10e6ce8748e17ba2c7598
  go version: go1.18.1-ts710a0d8610

 

The network is completely switched, not routed and we took care that tailscale actually used the switched connection using tailscale ping.

Test 0: Direct connection over switched network

Desktop running iperf -s, VM running iperf -c 10.9.2.10:

Connecting to host 10.9.2.10, port 5201
[  5] local 10.9.2.103 port 52944 connected to 10.9.2.10 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  94.7 MBytes   794 Mbits/sec  338    109 KBytes       
[  5]   1.00-2.00   sec  98.0 MBytes   822 Mbits/sec  353    148 KBytes       
[  5]   2.00-3.00   sec  96.6 MBytes   811 Mbits/sec  382    117 KBytes       
[  5]   3.00-4.00   sec   103 MBytes   862 Mbits/sec  334    116 KBytes       
[  5]   4.00-5.00   sec   101 MBytes   851 Mbits/sec  483    102 KBytes       
[  5]   5.00-6.00   sec   104 MBytes   874 Mbits/sec  503    126 KBytes       
[  5]   6.00-7.00   sec   105 MBytes   883 Mbits/sec  527    119 KBytes       
[  5]   7.00-8.00   sec   108 MBytes   906 Mbits/sec  451    105 KBytes       
[  5]   8.00-9.00   sec   108 MBytes   903 Mbits/sec  442    117 KBytes       
[  5]   9.00-10.00  sec   107 MBytes   900 Mbits/sec  461    123 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.00 GBytes   861 Mbits/sec  4274             sender
[  5]   0.00-10.00  sec  1.00 GBytes   860 Mbits/sec                  receiver

iperf Done.

VM running iperf -s, Desktop running iperf -c 10.9.2.103

Connecting to host 10.9.2.103, port 5201
[  5] local 10.9.2.10 port 42630 connected to 10.9.2.103 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  88.5 MBytes   742 Mbits/sec    0    966 KBytes       
[  5]   1.00-2.00   sec  90.0 MBytes   755 Mbits/sec    0   1.12 MBytes       
[  5]   2.00-3.00   sec  87.5 MBytes   734 Mbits/sec   33    833 KBytes       
[  5]   3.00-4.00   sec  90.0 MBytes   755 Mbits/sec    0    833 KBytes       
[  5]   4.00-5.00   sec  88.8 MBytes   745 Mbits/sec    0   1.00 MBytes       
[  5]   5.00-6.00   sec  88.8 MBytes   744 Mbits/sec    0   1.00 MBytes       
[  5]   6.00-7.00   sec  87.5 MBytes   734 Mbits/sec    0   1.09 MBytes       
[  5]   7.00-8.00   sec  90.0 MBytes   755 Mbits/sec    0   1.09 MBytes       
[  5]   8.00-9.00   sec  90.0 MBytes   755 Mbits/sec    0   1.09 MBytes       
[  5]   9.00-10.00  sec  90.0 MBytes   755 Mbits/sec   13    863 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   891 MBytes   747 Mbits/sec   46             sender
[  5]   0.00-10.00  sec   888 MBytes   745 Mbits/sec                  receiver

iperf Done.

The direction where the VM hosts the iperf -s server i.e. sends the data shows a slight degradation of performance

Test 1: Desktop running iperf -s, VM running iperf -c

Connecting to host 100.64.0.2, port 5201
[  5] local 100.64.0.3 port 37466 connected to 100.64.0.2 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  39.4 MBytes   330 Mbits/sec   62    149 KBytes       
[  5]   1.00-2.00   sec  45.8 MBytes   385 Mbits/sec   44    150 KBytes       
[  5]   2.00-3.00   sec  38.9 MBytes   326 Mbits/sec   97    122 KBytes       
[  5]   3.00-4.00   sec  47.9 MBytes   401 Mbits/sec    7    242 KBytes       
[  5]   4.00-5.00   sec  39.5 MBytes   332 Mbits/sec  118    110 KBytes       
[  5]   5.00-6.00   sec  46.6 MBytes   391 Mbits/sec   32    136 KBytes       
[  5]   6.00-7.00   sec  41.8 MBytes   351 Mbits/sec   42    159 KBytes       
[  5]   7.00-8.00   sec  44.3 MBytes   372 Mbits/sec   91    104 KBytes       
[  5]   8.00-9.00   sec  36.1 MBytes   303 Mbits/sec   72    133 KBytes       
[  5]   9.00-10.00  sec  41.5 MBytes   348 Mbits/sec   39    139 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   422 MBytes   354 Mbits/sec  604             sender
[  5]   0.00-10.00  sec   421 MBytes   353 Mbits/sec                  receiver

iperf Done.

Test 2: VM running iperf -s, Desktop running iperf -c

Connecting to host 100.64.0.3, port 5201
[  5] local 100.64.0.2 port 36744 connected to 100.64.0.3 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  23.7 MBytes   199 Mbits/sec  104   89.9 KBytes       
[  5]   1.00-2.00   sec  23.6 MBytes   198 Mbits/sec   80   49.2 KBytes       
[  5]   2.00-3.00   sec  21.1 MBytes   177 Mbits/sec   59   54.0 KBytes       
[  5]   3.00-4.00   sec  23.6 MBytes   198 Mbits/sec   68   69.6 KBytes       
[  5]   4.00-5.00   sec  19.1 MBytes   160 Mbits/sec   77   48.0 KBytes       
[  5]   5.00-6.00   sec  25.3 MBytes   212 Mbits/sec   76   62.4 KBytes       
[  5]   6.00-7.00   sec  21.4 MBytes   179 Mbits/sec   50    107 KBytes       
[  5]   7.00-8.00   sec  25.6 MBytes   215 Mbits/sec   35    124 KBytes       
[  5]   8.00-9.00   sec  22.5 MBytes   188 Mbits/sec   71   48.0 KBytes       
[  5]   9.00-10.00  sec  25.0 MBytes   209 Mbits/sec   42   64.8 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   231 MBytes   194 Mbits/sec  662             sender
[  5]   0.00-10.01  sec   230 MBytes   193 Mbits/sec                  receiver

UDP tests

UDP tests were mostly similar to TCP tests (albeit slightly higher throughput at up to 400 Mbit/s), including the sensitivity to the direction of the connection.

Interpretation of the results

Tailscale has significant impact on network speeds and will not regularly be able to achieve near-Gigabit iperf3 speeds given typical setup with Desktop that are a couple of years old, and virtual machines. However, achieving a throughput of 200-400 Mbit/s is more than enough for most applications.

Interestingly, the speed is highly dependent on the direction of transfer between a less powerful VM and a more powerful Desktop, with a factor of x1.5 … x2 between the two directions. This might be attributed to the amount of computation required to encrypt or decrypt the data.

Posted by Uli Köhler in Networking

Recommended library for executing shell commands in Python

I recommend using invoke instead of the built-in subprocess to handle executing any shell command in Python.

Not only does invoke‘s run() it provide a more user friendly syntax compared to e.g. subprocess.check_output():

run('make')

but it also tends to act more like you’d expect especially regarding the output of the command and has easy-to-use parameters such as hide=True to hide the output of shell commands.

Furthermore, it provides a buch of really useful features such as automatically responding to prompts from the shell command.

Posted by Uli Köhler in Python

How to enable USB-C host mode on Raspberry Pi

If you want to connect an USB device such as a 3D printer mainboard to your Raspberry Pi 4 using the USB-C connector as opposed to the larger USB-A connector, you need to first configure the Raspberry Pi kernel to use host mode for the USB-C connector.

To temporarily enable it:

sudo modprobe -r dwc2 && sudo dtoverlay dwc2 dr_mode=host && sudo modprobe dwc2

This method has the advantage of not requiring a reboot.

To permanently enable it:

Edit /boot/config.txt and add

dtoverlay=dwc2,dr_mode=host

at the end of the file (in the [all] section). Then

reboot

Posted by Uli Köhler in Raspberry Pi