How to install Netmaker client (netclient) on Ubuntu in 15 seconds

Just run these commands in your Shell in Ubuntu to install netclient

curl -sL 'https://apt.netmaker.org/gpg.key' | sudo tee /etc/apt/trusted.gpg.d/netclient.asc
curl -sL 'https://apt.netmaker.org/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/netclient.list
sudo apt update
sudo apt install netclient

Source: Netmaker installation page

Posted by Uli Köhler in Networking, VPN, Wireguard

What is the Reset Configuration PIN default for MikroTik RB2011UiAS-2HnD-IN?

When you try to reset the configuration of the MikroTik RB2011UiAS-2HnD-IN router via the integrated display, you will be asked for a PIN code for confirmation.

By default, this PIN code is 1234.

Posted by Uli Köhler in MikroTik, Networking

How to setup Netmaker using docker-compose in under 15 minutes

In this post, we’ll build a simple setup for running netmaker with PostgreSQL backend using docker-compose with an external Trik

First, create a directory for the Netmaker files to reside in, e.g.:

mkdir /opt/netmaker

cd to that directory:

cd /opt/netmaker

At this point we’ll download the Mosquitto config from Github and enable the ports in the firewall ufw. I reserved 1000 ports 51821:52821 in order to facilitate having a lot of networks (more than the default 9). My traefik config is the one from Simple Traefik docker-compose setup with Lets Encrypt Cloudflare DNS-01 & TLS-ALPN-01 & HTTP-01 challenges which allows having a single *.netmaker.mydomain.com Let’s Encrypt wildcard certificate.

Now, create docker-compose.yml in that directory

version: "3.4"

services:
  postgres:
    image: postgres
    restart: unless-stopped
    volumes:
      - ./pg_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=netmaker
      - POSTGRES_USER=netmaker
  netmaker: # The Primary Server for running Netmaker
    image: gravitl/netmaker:v0.14.5
    depends_on:
      - postgres
    cap_add:
      - NET_ADMIN
      - NET_RAW
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
    restart: always
    volumes: # Volume mounts necessary for sql, coredns, and mqtt
      - ./netmaker_dnsconfig:/root/config/dnsconfig
      - ./netmaker_sqldata:/root/data
      - ./netmaker_shared_certs:/etc/netmaker
    environment: # Necessary capabilities to set iptables when running in container
      SERVER_NAME: "broker.${NETMAKER_BASE_DOMAIN}" # The domain/host IP indicating the mq broker address
      SERVER_HOST: "${NETMAKER_PUBLIC_IP}" # Set to public IP of machine.
      SERVER_HTTP_HOST: "api.${NETMAKER_BASE_DOMAIN}" # Overrides SERVER_HOST if set. Useful for making HTTP available via different interfaces/networks.
      SERVER_API_CONN_STRING: "api.${NETMAKER_BASE_DOMAIN}:443"
      # COREDNS_ADDR: "${NETMAKER_PUBLIC_IP}" # Address of the CoreDNS server. Defaults to SERVER_HOST
      DNS_MODE: "off" # Enables DNS Mode, meaning all nodes will set hosts file for private dns settings.
      API_PORT: "8081" # The HTTP API port for Netmaker. Used for API calls / communication from front end. If changed, need to change port of BACKEND_URL for netmaker-ui.
      CLIENT_MODE: "on" # Depricated. CLIENT_MODE should always be ON
      REST_BACKEND: "on" # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST). Change to "off" to turn off.
      DISABLE_REMOTE_IP_CHECK: "off" # If turned "on", Server will not set Host based on remote IP check. This is already overridden if SERVER_HOST is set. Turned "off" by default.
      TELEMETRY: "on" # Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry.
      RCE: "off" # Enables setting PostUp and PostDown (arbitrary commands) on nodes from the server. Off by default.
      MASTER_KEY: "${NETMAKER_MASTER_KEY}" # The admin master key for accessing the API. Change this in any production installation.
      CORS_ALLOWED_ORIGIN: "*" # The "allowed origin" for API requests. Change to restrict where API requests can come from.
      DISPLAY_KEYS: "on" # Show keys permanently in UI (until deleted) as opposed to 1-time display.
      DATABASE: "postgres" # Database to use - sqlite, postgres, or rqlite
      SQL_HOST: "postgres"
      SQL_DB: "netmaker"
      SQL_USER: "netmaker"
      SQL_PASS: "${POSTGRES_PASSWORD}"
      NODE_ID: "${SERVER_NAME}" # used for HA - identifies this server vs other servers
      MQ_HOST: "mq"  # the address of the mq server. If running from docker compose it will be "mq". Otherwise, need to input address. If using "host networking", it will find and detect the IP of the mq container.
      MQ_SERVER_PORT: "1883" # the reachable port of MQ by the server - change if internal MQ port changes (or use external port if MQ is not on the same machine)
      MQ_PORT: "443" # the reachable port of MQ - change if external MQ port changes (port on proxy, not necessarily the one exposed in docker-compose)
      HOST_NETWORK: "off" # whether or not host networking is turned on. Only turn on if configured for host networking (see docker-compose.hostnetwork.yml). Will set host-level settings like iptables.
      VERBOSITY: "1" # logging verbosity level - 1, 2, or 3
      MANAGE_IPTABLES: "on" # deprecated
      # PORT_FORWARD_SERVICES: "ssh,mq" # decide which services to port forward ("dns","ssh", or "mq")
      # this section is for OAuth
      AUTH_PROVIDER: "" # "<azure-ad|github|google|oidc>"
      CLIENT_ID: "" # "<client id of your oauth provider>"
      CLIENT_SECRET: "" # "<client secret of your oauth provider>"
      FRONTEND_URL: "" # "https://dashboard.<netmaker base domain>"
      AZURE_TENANT: "" # "<only for azure, you may optionally specify the tenant for the OAuth>"
      OIDC_ISSUER: "" # https://oidc.yourprovider.com - URL of oidc provider
    ports:
      - "51821-52821:51821-52821/udp" # wireguard ports
    expose:
      - "8081" # api port
    labels: # only for use with traefik proxy (default)
      - traefik.enable=true
      - traefik.http.routers.netmaker-api.entrypoints=websecure
      - traefik.http.routers.netmaker-api.rule=Host(`api.${NETMAKER_BASE_DOMAIN}`)
      - traefik.http.routers.netmaker-api.service=netmaker-api
      - traefik.http.services.netmaker-api.loadbalancer.server.port=8081
      - "traefik.http.routers.netmaker-api.tls.certresolver=cloudflare"
      - "traefik.http.routers.netmaker-api.tls.domains[0].main=${NETMAKER_BASE_DOMAIN}"
      - "traefik.http.routers.netmaker-api.tls.domains[0].sans=*.${NETMAKER_BASE_DOMAIN}"
  netmaker-ui:  # The Netmaker UI Component
    container_name: netmaker-ui
    image: gravitl/netmaker-ui:v0.14.5
    depends_on:
      - netmaker
    links:
      - "netmaker:api"
    restart: always
    environment:
      BACKEND_URL: "https://api.${NETMAKER_BASE_DOMAIN}" # URL where UI will send API requests. Change based on SERVER_HOST, SERVER_HTTP_HOST, and API_PORT
    expose:
      - "80"
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.nmui-security.headers.accessControlAllowOriginList=*.${NETMAKER_BASE_DOMAIN}
      - traefik.http.middlewares.nmui-security.headers.stsSeconds=31536000
      - traefik.http.middlewares.nmui-security.headers.browserXssFilter=true
      - traefik.http.middlewares.nmui-security.headers.customFrameOptionsValue=SAMEORIGIN
      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.X-Robots-Tag=none
      - traefik.http.middlewares.nmui-security.headers.customResponseHeaders.Server= # Remove the server name
      - traefik.http.routers.netmaker-ui.entrypoints=websecure
      - traefik.http.routers.netmaker-ui.middlewares=nmui-security@docker
      - traefik.http.routers.netmaker-ui.rule=Host(`dashboard.${NETMAKER_BASE_DOMAIN}`)
      - traefik.http.routers.netmaker-ui.service=netmaker-ui
      - traefik.http.services.netmaker-ui.loadbalancer.server.port=80
      - "traefik.http.routers.netmaker-ui.tls.certresolver=cloudflare"
      - "traefik.http.routers.netmaker-ui.tls.domains[0].main=${NETMAKER_BASE_DOMAIN}"
      - "traefik.http.routers.netmaker-ui.tls.domains[0].sans=*.${NETMAKER_BASE_DOMAIN}"
  mq: # the MQTT broker for netmaker
    container_name: mq
    image: eclipse-mosquitto:2.0.11-openssl
    depends_on:
      - netmaker
    restart: unless-stopped
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf # need to pull conf file from github before running (under docker/mosquitto.conf)
      - ./mosquitto_data:/mosquitto/data
      - ./mosquitto_logs:/mosquitto/log
      - ./netmaker_shared_certs:/mosquitto/certs
    expose:
      - "8883"
    labels:
      - traefik.enable=true
      - traefik.tcp.routers.mqtts.rule=HostSNI(`broker.${NETMAKER_BASE_DOMAIN}`)
      - traefik.tcp.routers.mqtts.tls.passthrough=true
      - traefik.tcp.services.mqtts-svc.loadbalancer.server.port=8883
      - traefik.tcp.routers.mqtts.service=mqtts-svc
      - traefik.tcp.routers.mqtts.entrypoints=websecure
      - "traefik.tcp.routers.mqtts.tls.certresolver=cloudflare"
      - "traefik.tcp.routers.mqtts.tls.domains[0].main=${NETMAKER_BASE_DOMAIN}"
      - "traefik.tcp.routers.mqtts.tls.domains[0].sans=*.${NETMAKER_BASE_DOMAIN}"

After that, create .env in said directory containing some info about your node:

SERVER_NAME=netmaker-01
NETMAKER_BASE_DOMAIN=netmaker.mydomain.com
NETMAKER_MASTER_KEY=geeveeBaeQuie1cie6aaz6eepahleo
NETMAKER_PUBLIC_IP=101.252.11.54
POSTGRES_PASSWORD=ahph8Jih4aesheel7id1uyo0gietai

Adjust the NETMAKER_BASE_DOMAIN and the NETMAKER_PUBLIC_IP accordingly. Also, you need to choose a random NETMAKER_MASTER_KEY and POSTGRES_PASSWORD.

Now we’ll use the script from Create a systemd service for your docker-compose project in 10 seconds in order to create a systemd service to automatically run the service:

curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin

This script will also automatically start the service (i.e. docker-compose up).

 

Posted by Uli Köhler in Container, Docker

iperf3 benchmark of ZeroTier vs Netmaker vs Tailscale vs direct switched connection

In our setup, a virtual machine (running on an XCP-NG host) on  was connected to my Desktop (HP Z240, i7-6700 @3.4 GHz running Ubuntu 22.04) in a purely switched network with 1Gbit links. Both devices were connected using a MikroTik 10G switch (Marvell chip

I ran iperf3 -s on the VM and ran iperf3 -c [IP address] on the desktop. Reverse tests have not been performed.

Direct switched connection (no VPN)

Connecting to host 10.9.2.103, port 5201
[  5] local 10.9.2.10 port 56848 connected to 10.9.2.103 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  92.8 MBytes   779 Mbits/sec    0    444 KBytes       
[  5]   1.00-2.00   sec  90.7 MBytes   761 Mbits/sec    0    543 KBytes       
[  5]   2.00-3.00   sec  88.6 MBytes   743 Mbits/sec    0    816 KBytes       
[  5]   3.00-4.00   sec  90.0 MBytes   755 Mbits/sec    0    816 KBytes       
[  5]   4.00-5.00   sec  90.0 MBytes   755 Mbits/sec    0    856 KBytes       
[  5]   5.00-6.00   sec  88.8 MBytes   744 Mbits/sec    0    946 KBytes       
[  5]   6.00-7.00   sec  88.8 MBytes   745 Mbits/sec    0    946 KBytes       
[  5]   7.00-8.00   sec  90.0 MBytes   755 Mbits/sec    0    993 KBytes       
[  5]   8.00-9.00   sec  90.0 MBytes   755 Mbits/sec    0    993 KBytes       
[  5]   9.00-10.00  sec  88.8 MBytes   744 Mbits/sec    0    993 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   898 MBytes   754 Mbits/sec    0             sender
[  5]   0.00-10.01  sec   896 MBytes   751 Mbits/sec                  receiver

ZeroTier

Connecting to host 10.80.246.34, port 5201
[  5] local 10.80.246.38 port 35474 connected to 10.80.246.34 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  59.9 MBytes   503 Mbits/sec  338    102 KBytes       
[  5]   1.00-2.00   sec  60.2 MBytes   505 Mbits/sec  313    188 KBytes       
[  5]   2.00-3.00   sec  63.9 MBytes   536 Mbits/sec  176   99.3 KBytes       
[  5]   3.00-4.00   sec  74.3 MBytes   623 Mbits/sec  174    113 KBytes       
[  5]   4.00-5.00   sec  67.7 MBytes   568 Mbits/sec  197   83.2 KBytes       
[  5]   5.00-6.00   sec  72.5 MBytes   609 Mbits/sec  218    228 KBytes       
[  5]   6.00-7.00   sec  61.3 MBytes   514 Mbits/sec  281   77.8 KBytes       
[  5]   7.00-8.00   sec  72.0 MBytes   604 Mbits/sec  213   91.2 KBytes       
[  5]   8.00-9.00   sec  65.4 MBytes   549 Mbits/sec  309    156 KBytes       
[  5]   9.00-10.00  sec  53.9 MBytes   453 Mbits/sec  190    121 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   651 MBytes   546 Mbits/sec  2409             sender
[  5]   0.00-10.01  sec   650 MBytes   545 Mbits/sec                  receiver

NetMaker

Netmaker internally uses a normal (kernel-based) wireguard connection, so in some respect this is a test of Wireguard performance

Connecting to host 10.230.113.3, port 5201
[  5] local 10.230.113.1 port 35534 connected to 10.230.113.3 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   105 MBytes   881 Mbits/sec    0   1.01 MBytes       
[  5]   1.00-2.00   sec   104 MBytes   870 Mbits/sec   86    422 KBytes       
[  5]   2.00-3.00   sec   101 MBytes   849 Mbits/sec    0    488 KBytes       
[  5]   3.00-4.00   sec  98.8 MBytes   828 Mbits/sec    0    535 KBytes       
[  5]   4.00-5.00   sec  98.8 MBytes   828 Mbits/sec    0    584 KBytes       
[  5]   5.00-6.00   sec   104 MBytes   870 Mbits/sec    0    615 KBytes       
[  5]   6.00-7.00   sec  97.5 MBytes   818 Mbits/sec    7    472 KBytes       
[  5]   7.00-8.00   sec   104 MBytes   870 Mbits/sec    0    522 KBytes       
[  5]   8.00-9.00   sec   101 MBytes   849 Mbits/sec    0    580 KBytes       
[  5]   9.00-10.00  sec   102 MBytes   860 Mbits/sec    0    606 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1016 MBytes   852 Mbits/sec   93             sender
[  5]   0.00-10.00  sec  1014 MBytes   850 Mbits/sec                  receiver

 Tailscale

Tailscale 1.28.0 has been used for this test.

During this test, I ensured that the tailscale connection was established using the switched network and was not going through a DERP server or the routed network.

$ tailscale ping 100.64.0.3
pong from vm (fd5d:7b60:4742::3) via 10.9.2.103:41641 in 1ms

Results:

Connecting to host 100.64.0.3, port 5201
[  5] local 100.64.0.2 port 40690 connected to 100.64.0.3 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  38.3 MBytes   321 Mbits/sec  389   60.0 KBytes       
[  5]   1.00-2.00   sec  37.6 MBytes   315 Mbits/sec  366   43.2 KBytes       
[  5]   2.00-3.00   sec  36.7 MBytes   308 Mbits/sec  431   52.8 KBytes       
[  5]   3.00-4.00   sec  38.5 MBytes   323 Mbits/sec  488   80.3 KBytes       
[  5]   4.00-5.00   sec  29.3 MBytes   246 Mbits/sec  356   38.4 KBytes       
[  5]   5.00-6.00   sec  31.0 MBytes   260 Mbits/sec  351   86.3 KBytes       
[  5]   6.00-7.00   sec  27.1 MBytes   227 Mbits/sec  287   50.4 KBytes       
[  5]   7.00-8.00   sec  26.1 MBytes   219 Mbits/sec  210   46.8 KBytes       
[  5]   8.00-9.00   sec  27.1 MBytes   227 Mbits/sec  261   39.6 KBytes       
[  5]   9.00-10.00  sec  27.5 MBytes   231 Mbits/sec  222   40.8 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec   319 MBytes   268 Mbits/sec  3361             sender
[  5]   0.00-10.01  sec   318 MBytes   267 Mbits/sec                  receiver

Summary

The approximate performance expectation in this specific scenario is:

  • Tailscale: 300 Mbit/s
  • ZeroTier: 550 Mbit/s
  • Netmaker: 850 Mbit/s
  • Direct switched network: 750 Mbit/s

Curiously, netmaker performed better than the direct connection. The reason for this is not known at this point, but a similar effect has been observed in this medium.com article.

Generally, one can see that Tailscale (which internally uses software wireguard) is approximately half the speed of ZeroTier, which in turn is outperformed significantly by Netmaker.

In a followup post I will describe advantages and disadvantages of those solutions and explore under which scenarios I would use the solutions.

Posted by Uli Köhler in Headscale, Networking, Wireguard, ZeroTier

How to join network using zerotier-cli on Linux

Joining a network is as simple as

sudo zerotier-cli join [network-id]

for example:

sudo zerotier-cli join 9ecbaef5759219ad

 

Posted by Uli Köhler in Networking, ZeroTier

How to install ZeroTier client on Ubuntu in 10 seconds

Run this command to install the ZeroTier client on any recent Ubuntu version. Tested on Ubuntu 22.04

curl -s https://install.zerotier.com | sudo bash

Source: ZeroTier installation website

Posted by Uli Köhler in Networking, ZeroTier

How to fix Netmaker Fatal: Unable to initialize iptables on host: lookup coredns: Try again

Problem:

While starting up a dockerized netmaker instance, you see the following error message:

[netmaker] Fatal: Unable to initialize iptables on host: lookup coredns: Try again 

Solution:

In the docker-compose.yml or other environment config for netmaker, set

DNS_MODE: "off"

and comment out PORT_FORWARD_SERVICES

# PORT_FORWARD_SERVICES: "ssh,mq"

After that, you possibly need to remove the netmaker data directories (volumes). After doing a clean start, netmaker should work fine.

Posted by Uli Köhler in Networking

How to setup Wekan using docker-compose in just 3 minutes

First, create a directory for the Wekan files to reside in, e.g.:

mkdir /opt/wekan

Change to that directory:

cd /opt/wekan

Now, we need to create the data directory which needs to be owned by UID 999: in order for Wekan to store uploads:

mkdir -p wekan_data && chown -R 999:999 wekan_data

Now, create docker-compose.yml in that directory

version: '3.4'

services:
  wekandb:
    restart: always
    image: mongo:5
    command: mongod --logpath /dev/null --oplogSize 128 --quiet
    healthcheck:
      test: ["CMD", "mongo", "--quiet", "--eval", "'quit(db.runCommand({ ping: 1 }).ok ? 0 : 2)'"]
      interval: 15s
      timeout: 10s
      retries: 3
      start_period: 10s
    volumes:
      - ./wekan-db:/data/db
      - ./wekan-db-dump:/dump
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro


  wekan:
    image: quay.io/wekan/wekan
    restart: always
    ports:
      - 7972:8080
    environment:
      - MONGO_URL=mongodb://wekandb:27017/wekan
      - ROOT_URL=${URL}
      - MAIL_URL=${EMAIL_URL}
      - MAIL_FROM=${EMAIL_FROM}
      - WITH_API=true
      - WRITABLE_PATH=/data
    volumes:
      - ./wekan_data:/data
      - /etc/localtime:/etc/localtime:ro
    depends_on:
      - wekandb

After that, create .env in said directory containing some info about your node:

URL=https://wekan.mydomain.com
EMAIL_URL=smtp://noreply%40mydomain.com:[email protected]:25/
EMAIL_FROM='My Wekan <[email protected]>'

Now we’ll use the script from Create a systemd service for your docker-compose project in 10 seconds in order to create a systemd service to automatically run the service:

curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin

This script will also automatically start the service (i.e. docker-compose up).

Now you can setup your reverse proxy to point your domain – e.g. wekan.techoverflow.net to port 7972 (or change that port in docker-compose.yml). Which reverse proxy you use doesn’t matter all too much, I use both nginx and traefik. I will cover the configuration for these reverse proxies in future posts.

Once you can access Wekan, you can register as a new user. The first user will be an admin and can also disable registration using the Web UI.

Posted by Uli Köhler in Container, Docker

How to fix certbot error: unrecognized arguments: –dns-cloudflare-credentials

Problem:

While trying to request a Let’s encrypt certificate using certbot with the DNS-01 challenge using the Cloudflare DNS provider, you see the following error message:

certbot: error: unrecognized arguments: --dns-cloudflare-credentials /etc/letsencrypt/cloudflare-akamba.ini

Solution:

You need to install the cloudflare plugin for certbot using

sudo pip3 install --upgrade certbot-dns-cloudflare

After that, certbot will recognize the --dns-cloudflare-credentials command line option

Posted by Uli Köhler in Networking

How to fix certbot ContextualVersionConflict: zope.interface 4.7.1, Requirement.parse(‘zope.interface>=5.3.0a1’)

Problem:

While running certbot , you see an error message like the following one:

An unexpected error occurred:
pkg_resources.ContextualVersionConflict: (zope.interface 4.7.1 (/usr/lib/python3/dist-packages), Requirement.parse('zope.interface>=5.3.0a1'), {'zope.component'})

Solution:

Your installed version of zope.interface is too old. Upgrade it using

sudo pip3 install --upgrade zope.interface

After that, certbot should work fine.

Posted by Uli Köhler in Networking

How to fix wekan Path “/data/attachments” is not writable!

Problem:

While starting your dockerized wekan setup, you see an error message like this:

wekan_1    | errorClass [Error]: [FilesCollection.attachments] Path "/data/attachments" is not writable! [401]
wekan_1    |     at new FilesCollection (packages/ostrio:files/server.js:354:15)
wekan_1    |     at module (models/attachments.js:52:15)
wekan_1    |     at fileEvaluate (packages/modules-runtime.js:336:7)
wekan_1    |     at Module.require (packages/modules-runtime.js:238:14)
wekan_1    |     at Module.moduleLink [as link] (/build/programs/server/npm/node_modules/meteor/modules/node_modules/@meteorjs/reify/lib/runtime/index.js:52:22)
wekan_1    |     at module (server/publications/attachments.js:1:24)
wekan_1    |     at fileEvaluate (packages/modules-runtime.js:336:7)
wekan_1    |     at Module.require (packages/modules-runtime.js:238:14)
wekan_1    |     at require (packages/modules-runtime.js:258:21)
wekan_1    |     at /build/programs/server/app/app.js:162362:1
wekan_1    |     at /build/programs/server/boot.js:401:38
wekan_1    |     at Array.forEach (<anonymous>)
wekan_1    |     at /build/programs/server/boot.js:226:21
wekan_1    |     at /build/programs/server/boot.js:464:7
wekan_1    |     at Function.run (/build/programs/server/profile.js:280:14)
wekan_1    |     at /build/programs/server/boot.js:463:13 {
wekan_1    |   isClientSafe: true,
wekan_1    |   error: 401,
wekan_1    |   reason: '[FilesCollection.attachments] Path "/data/attachments" is not writable!',
wekan_1    |   details: Error: EACCES: permission denied, mkdir '/data/attachments'
wekan_1    |       at Object.mkdirSync (fs.js:1014:3)
wekan_1    |       at new FilesCollection (packages/ostrio:files/server.js:348:10)
wekan_1    |       at module (models/attachments.js:52:15)
wekan_1    |       at fileEvaluate (packages/modules-runtime.js:336:7)
wekan_1    |       at Module.require (packages/modules-runtime.js:238:14)
wekan_1    |       at Module.moduleLink [as link] (/build/programs/server/npm/node_modules/meteor/modules/node_modules/@meteorjs/reify/lib/runtime/index.js:52:22)
wekan_1    |       at module (server/publications/attachments.js:1:24)
wekan_1    |       at fileEvaluate (packages/modules-runtime.js:336:7)
wekan_1    |       at Module.require (packages/modules-runtime.js:238:14)
wekan_1    |       at require (packages/modules-runtime.js:258:21)
wekan_1    |       at /build/programs/server/app/app.js:162362:1
wekan_1    |       at /build/programs/server/boot.js:401:38
wekan_1    |       at Array.forEach (<anonymous>)
wekan_1    |       at /build/programs/server/boot.js:226:21
wekan_1    |       at /build/programs/server/boot.js:464:7
wekan_1    |       at Function.run (/build/programs/server/profile.js:280:14)
wekan_1    |       at /build/programs/server/boot.js:463:13 {
wekan_1    |     errno: -13,
wekan_1    |     syscall: 'mkdir',
wekan_1    |     code: 'EACCES',
wekan_1    |     path: '/data/attachments'
wekan_1    |   },
wekan_1    |   errorType: 'Meteor.Error'
wekan_1    | }

Solution:

You have mounted a local directory as wekan data directory, for example like this in docker-compose.yml:

wekan:
  image: quay.io/wekan/wekan
  /* ... */
  environment:
    /*...*/
    - WRITABLE_PATH=/data
  volumes:
    - ./wekan_data:/data

But this directory does not have the correct permissions set. You can fix it using this command on the directory (wekan_data in this example):

sudo chown -R 999:999 wekan_data

After that, restart wekan and the issue should be fixed.

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

How to setup ZeroTier One & ZTNCUI using docker-compose in just 2 minutes

First, create a directory for the ZeroTier One / ZTNCUI files to reside in, e.g.:

mkdir /opt/zerotier-mydomain

Now, create docker-compose.yml in that directory

version: '3.4'

services:
  ztncui:
    container_name: ztncui
    restart: always
    image: keynetworks/ztncui
    ports:
      - 9993:9993/udp
      - 3180:3180
      - 3443:3443
    volumes:
      - ./etc:/opt/key-networks/ztncui/etc
      - ./zt1:/var/lib/zerotier-one   

After that, create .env in said directory containing some info about your node:

NODE_ENV=production
HTTPS_PORT=3443
MYDOMAIN=zerotier.mydomain.com

Now we’ll use the script from Create a systemd service for your docker-compose project in 10 seconds in order to create a systemd service to automatically run the service:

curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin

This script will also automatically start the service (i.e. docker-compose up). ZTNCUI (which comes packaged with ZeroTier One) will generate a temporary admin password automatically, which we can extract from the log using this simple command:

docker-compose exec ztncui cat /var/log/docker-ztncui.log | grep "Current Password" | tail -n 1

Example output:

2022/08/19 14:32:37 Current Password: esh0Eengai

Be sure to open the ports 9993/udp, 3180 and (unless you are using a reverse proxy) 3443 in your firewall, for example:

sudo ufw allow 9993/udp
sudo ufw allow 3180
sudo ufw allow 3443

Now we can open https://[IP]:3443 to open the webinterface (ignore the certificate validation error). You can also setup a reverse proxy at this stage, which we’ll cover in future posts.

You should see a page like this one:

Click Login at the top right:

Enter admin as username and the password we extracted above (esh0Eengai in this example).

You will be asked to change your password, and after that you can create ZeroTier networks.

Posted by Uli Köhler in Allgemein, Networking, ZeroTier

How to save image from Jupyter Notebook using Right-Click

When you right-click on a plot in Jupyter, you will see the Jupyter Menu popup instead of the normal right-click menu which would allow you to save the image to your computer or copy it to the clipboard.

However, there’s an easy workaround: You can just Shift+Right click to see the normal right-click menu and then save the image to a file.

Posted by Uli Köhler in Jupyter

How to always get latest frame from OpenCV VideoCapture in Python

When working with OpenCV video capture, but when you only occasionally use images, you will get older images from the capture buffer.

This code example solves this issue by running a separate capture thread that continually saves images to a temporary buffer.

Therefore, you can always get the latest image from the buffer. The code is based on our basic example How to take a webcam picture using OpenCV in Python

video_capture = cv2.VideoCapture(0)

video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

if not video_capture.isOpened():
    raise Exception("Could not open video device")

class TakeCameraLatestPictureThread(threading.Thread):
    def __init__(self, camera):
        self.camera = camera
        self.frame = None
        super().__init__()
        # Start thread
        self.start()

    def run(self):
        while True:
            ret, self.frame = self.camera.read()

latest_picture = TakeCameraLatestPictureThread(video_capture)

Usage example:

# Convert latest image to the correct colorspace
rgb_img = cv2.cvtColor(latest_picture.frame, cv2.COLOR_BGR2RGB)
# Show
plt.imshow(rgb_img)

 

 

Posted by Uli Köhler in Audio/Video, OpenCV, Python

How to capture single PNG image using fswebcam

fswebcam -r 1920x1080 --png 9 -d /dev/video0 -D 0 test.png

where:

  • -r 1920x1080 is the resolution of the image. The camera must support this. In order to see supported resolutions, see How to list USB camera video formats using v4l2-ctl
  • --png 9 means output PNG with quality 9PNG is lossless, so the quality is just the compression factor. 9 means accept higher CPU consumption for slightly smaller filesize.
  • -d /dev/video0 means to use the camera in /dev/video0
  • -D 0 means no delay before capturing the image
  • test.png means: Write the image to test.png.
Posted by Uli Köhler in Audio/Video

How to list USB camera video formats using v4l2-ctl

v4l2-ctl --device /dev/video0 --list-formats-ext

 

Posted by Uli Köhler in Audio/Video

How to view software RAID status on Linux

You can simply view /proc/mdstat:

cat /proc/mdstat

Example output

Personalities : [raid1] [raid6] [raid5] [raid4] [linear] [multipath] [raid0] [raid10] 
md2 : active raid5 sdb3[4] sdd3[0] sda3[2] sdc3[1]
      2111434752 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/3] [UUU_]
      [===>.................]  recovery = 16.2% (114244316/703811584) finish=48.8min speed=201053K/sec
      bitmap: 2/6 pages [8KB], 65536KB chunk

md1 : active raid1 sdb2[3] sda2[2] sdd2[0] sdc2[1]
      1046528 blocks super 1.2 [4/4] [UUUU]
      
md3 : active raid5 sdb4[4] sda4[2] sdd4[0] sdc4[1]
      44661594624 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/3] [UUU_]
        resync=DELAYED
      bitmap: 2/111 pages [8KB], 65536KB chunk

md0 : active raid1 sda1[2] sdb1[3] sdd1[0] sdc1[1]
      33520640 blocks super 1.2 [4/4] [UUUU]
      
unused devices: <none>

 

Posted by Uli Köhler in Linux

How to fix apt error: NO_PUBKEY 1F3045A5DF7587C3

Problem:

When running apt update, you see the following error message:

Err:14 https://repo.skype.com/deb stable InRelease
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 1F3045A5DF7587C3

Solution:

This error occurs due to the Skype repo using the outdated (as of April 2022) apt-key method of importing keys.

You can import the key using the following commands:

wget -qO- https://repo.skype.com/data/SKYPE-GPG-KEY | gpg --dearmor | sudo tee /usr/share/keyrings/skype-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/skype-archive-keyring.gpg] https://repo.skype.com/deb stable main" > /etc/apt/sources.list.d/skype-stable.list

and then running

apt update

again.

 

Posted by Uli Köhler in Linux

How to fix apt error: NO_PUBKEY C5E224500C1289C0

Problem:

When running apt update, you see the following error message:

Err:2 https://linux.teamviewer.com/deb stable InRelease                                                                                                                                
  The following signatures couldn't be verified because the public key is not available: NO_PUBKEY C5E224500C1289C0

Solution:

This error occurs due to the teamviewer repo using the outdated (as of April 2022) apt-key method of importing keys.

You can import the key using the following commands:

wget -q https://download.teamviewer.com/download/linux/signature/TeamViewer2017.asc -O- | gpg --dearmor | sudo tee /usr/share/keyrings/teamviewer-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/teamviewer-archive-keyring.gpg] https://linux.teamviewer.com/deb stable main" > /etc/apt/sources.list.d/teamviewer.list

and then running

apt update

again.

 

Posted by Uli Köhler in Linux

How to write std::string to ESP32 NVS

Also see: How to read ESP32 NVS value into std::string

Writing a std::string into NVS is pretty easy. You can just use .c_str() to obtain a const char* pointer and then proceed as shown in How to write string (const char*) to ESP32 NVS

You can use this utility function to write a string (const char*) into the NVS:

bool NVSWriteString(nvs_handle_t nvs, const std::string& key, const std::string& value) {
    esp_err_t err;
    if((err = nvs_set_blob(nvs, key.c_str(), value.data(), value.size())) != ESP_OK) {
        Serial.printf("Failed to write NVS key %s: %srn", key, esp_err_to_name(err));
        return false;
    }
    return true;
}

Usage example

This example is based on How to initialize NVS on ESP32. Most importantly, it assumes you have already initialized the NVS and myNVS exists and is valid.

char mySerialNo[128] = {0};

_NVS_WriteString(myNVS, "SerialNo", "0000001");

This will write the value 0000001 to the NVS with key SerialNo

Note that NVS strings are length-delimited (as in: The length is stored in the NVS) and not neccessarily NUL-terminated. This code does not store the NUL terminator in NVS.

Posted by Uli Köhler in ESP8266/ESP32
This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Cookie settingsACCEPTPrivacy &amp; Cookies Policy