Reverse Proxy Protection by Authentik

auth_basic is a lousy protection for your websites. Better use en SSO solution like Authentik. Here is my NGINX reverse proxy host configuration:

server {
    server_name HOST.DOMAIN.NAME;
    access_log /var/log/nginx/HOST.DOMAIN.NAME.access.log;
    error_log /var/log/nginx/HOST.DOMAIN.NAME.error.log;
    
    # Increase buffer size for large headers from Authentik
    proxy_buffers 8 16k;
    proxy_buffer_size 32k;
    
    # All requests to /outpost.goauthentik.io must be accessible without authentication
    location /outpost.goauthentik.io {
        # When using the embedded outpost, proxy to Authentik backend
        proxy_pass http://AUTHENTIK_IP:9000/outpost.goauthentik.io;
        
        # CRITICAL: Set Host to auth.DOMAIN.NAME so Authentik knows which provider to use
        proxy_set_header Host auth.DOMAIN.NAME;
        
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
        add_header Set-Cookie $auth_cookie;
        auth_request_set $auth_cookie $upstream_http_set_cookie;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
    }
    
    # Special location for when the /auth endpoint returns a 401
    # For domain level, redirect to your authentik server with the full redirect path
    location @goauthentik_proxy_signin {
        internal;
        add_header Set-Cookie $auth_cookie;
        # CHANGED: Use full auth.DOMAIN.NAME URL for domain-level auth
        return 302 https://auth.DOMAIN.NAME/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
    }
    
    location / {
        # -------------------------
        # BYPASS AUTH FOR LOCAL IPS
        # -------------------------
        satisfy any;
        allow 192.168.1.0/24;
        deny all;

        ##############################
        # authentik-specific config
        ##############################
        auth_request /outpost.goauthentik.io/auth/nginx;
        error_page 401 = @goauthentik_proxy_signin;
        auth_request_set $auth_cookie $upstream_http_set_cookie;
        add_header Set-Cookie $auth_cookie;

        # Translate headers from the outposts back to the actual upstream
        auth_request_set $authentik_username $upstream_http_x_authentik_username;
        auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
        auth_request_set $authentik_entitlements $upstream_http_x_authentik_entitlements;
        auth_request_set $authentik_email $upstream_http_x_authentik_email;
        auth_request_set $authentik_name $upstream_http_x_authentik_name;
        auth_request_set $authentik_uid $upstream_http_x_authentik_uid;

        proxy_set_header X-authentik-username $authentik_username;
        proxy_set_header X-authentik-groups $authentik_groups;
        proxy_set_header X-authentik-entitlements $authentik_entitlements;
        proxy_set_header X-authentik-email $authentik_email;
        proxy_set_header X-authentik-name $authentik_name;
        proxy_set_header X-authentik-uid $authentik_uid;
        
        # Your original proxy settings to Homer
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        
        # Support for websocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        proxy_pass http://AUTHENTIK_IP:8090;
     }
    
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/HOST.DOMAIN.NAME/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/HOST.DOMAIN.NAME/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

server {
    if ($host = HOST.DOMAIN.NAME) {
        return 301 https://$host$request_uri;
    }
    listen 80;
    server_name HOST.DOMAIN.NAME;
    return 404;
}

In Authentik, you have to create a Provider:

In Authentik, create an Application:

Funkwhale: installation

Funkwhale is a very nice audio hosting platform. We’ll install it using docker-compose in Portainer.

services:
  funkwhale:
    image: funkwhale/all-in-one:latest
    container_name: funkwhale2 #I have 2 instances of funkwhale on this docker host
    restart: unless-stopped
    depends_on:
      - postgres
      - redis
    environment:
      - FUNKWHALE_HOSTNAME=audio.MYSITE.COM
      - FUNKWHALE_PROTOCOL=https
      - FUNKWHALE_PROTOCOL_HEADER=TRUE
      - FUNKWHALE_PROXY_BODY_SIZE=200M
      - FUNKWHALE_MAX_UPLOAD_SIZE=200M
      - DATABASE_URL=postgresql://funkwhale:XXX_MY_DB_PWD_XXX@postgres:5432/funkwhale
      - REDIS_URL=redis://redis:6379/0
      - DJANGO_SECRET_KEY=XXXXX_MYSECRET_XXXXX
      - FUNKWHALE_ENABLE_FEDERATION=false
      - FUNKWHALE_DISABLE_REGISTRATION=true
      # Email Configuration (for GMAIL)
      - EMAIL_CONFIG=smtp+tls://MY_GMAIL_ADDRESS@gmail.com:XX_MY_APP_PASSWORD_XX@smtp.gmail.com:587
      - DEFAULT_FROM_EMAIL=MY_GMAIL_ADDRESS@gmail.com
    volumes:
      - /mnt/funkwhale2-data/media:/data/media
      - /mnt/funkwhale2-data/static:/data/static
      - /mnt/funkwhale2-data/music:/music
    networks:
      - funkwhale2
    ports:
      - "5001:80"  # Choose e free host port; default is 5000
      
  postgres:
    image: postgres:14
    container_name: funkwhale2_postgres
    restart: unless-stopped
    environment:
      - POSTGRES_USER=funkwhale
      - POSTGRES_PASSWORD=XXX_MY_DB_PWD_XXX
      - POSTGRES_DB=funkwhale
    volumes:
      - ./postgres2:/var/lib/postgresql/data  # Different volume!
    networks:
      - funkwhale2
      
  redis:
    image: redis:7-alpine
    container_name: funkwhale2_redis
    restart: unless-stopped
    networks:
      - funkwhale2
      
networks:
  funkwhale2:  # I run 2 instances of Funkwhale on my docker.
    external: false

Create your Django secret (or passwords) with this:

openssl rand -base64 48

Funkwhale user admin

To check if users are active in database, or if they confirmed the registration email, start by connecting to the postgres database on your docker host:

docker exec -it funkwhale2_postgres psql -U funkwhale -d funkwhale

Confirm registration & confirmation status:

SELECT
  id,
  username,
  email,
  is_active,
  date_joined,
  last_login
FROM users_user
ORDER BY date_joined DESC;

will output something like:

 id | username |          email          | is_active |          date_joined          |          last_login           
----+----------+-------------------------+-----------+-------------------------------+-------------------------------
  4 | Paul     | paulXXX@domain.com      | t         | 2025-12-14 11:18:31.248306+00 | 2025-12-14 11:18:32.49417+00
  3 | Mary     | maryXYZ@domain.com      | t         | 2025-12-13 23:33:18.022068+00 | 2025-12-13 23:33:20.309663+00
  1 | admin    | siteadmin@domain.com    | t         | 2025-12-13 23:13:51.537683+00 | 2025-12-13 23:32:35.71689+00
(3 rows)

Find users who registered but never logged in:

SELECT username, email
FROM users_user
WHERE last_login IS NULL;

Find users who registered but never confirmed:

SELECT username, email
FROM users_user
WHERE is_active = false;

Setlist

#TitleArtistBPMKey
original
Key
played
Guitar Tuning
Capo
Transp
Keys
1One Horse TownBlackberry Smoke120CC-1
2Fire AwayChris Stapleton90GA-1
3Ain’t no Love in OklahomaLuke CombsCmCm0
4Country must be Country WideBrantley Gilbert83C#m-1
5Sweet Home AlabamaLynyrd Skynyrd
6Keep the wolf awayUncle Lucius
7Beer never broke my heartLuke Combs
8Tennessee WhiskeyChris Stapleton
9Man of constant SorrowBlackberry Smoke130Gm
10I think I’m in Love with YouChris Stapleton
11Change on the RiseAvi Kaplan
12Sleeping in the black topColter Wall
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Quick action: convert to m4a

If you do not want to keep large WAV or AIFF files on your disk that eat up precious space, you can use Apple’s Automator to write a quick action to easily convert these files to m4a.

Open Apple’s Automator and create a new quick action.

Automator: convert audio files to m4a
for f in "$@"
do
  afconvert -f m4af -d aac "$f" "${f%.*}.m4a"
done

You can also have Automator delete the original file after conversion:

Automator: convert audio to m4a and delete original file
for f in "$@"
do
  # Convert WAV to M4A
  afconvert -f m4af -d aac "$f" "${f%.*}.m4a"
  
  # Check if conversion succeeded before deleting
  if [ $? -eq 0 ]; then
    rm "$f"
  else
    echo "Conversion failed for $f, original not deleted."
  fi
done

These quick actions also work on video files (like mp4), in case you are only interested in keeping the audio track of a video file.

The above quick action workflows can be downloaded below:

To install the workflows as quick actions, you can download the above files, unzip and execute. macOs will then install the workflows. Your macOS security protection still keeps them disabled. You have to enable them in system preferences. Open Login Items & Extensions, under Extensions, behind the Finder line, hit the “i” button:

macOS system settings: enable the quick action

Source: These scripts have been developed with the help of a generative large language model.

Luxembourg’s smart meter SMARTY

Luxembourg’s smart meter branded as Smarty by the electric grid managing company CREOS allows monitoring your electricity consumption and production as well as your gas consumption. Additionally it allows the grid operator to control certain electrical appliances in your household.

Shedding relays – relais de délestage

If parts of the electricity grid in Luxembourg are becoming overloaded or unstable, the grid operator can remotely command the Smarty smart meter to take action to protect the grid. Smarty can diminish the power of domestic car charging points from 11kW to 7kW. As of writing this, CREOS confirms not using this feature (January 2025).

Smarty can also reduce the power of photovoltaic systems to about a third of the available power, if your PV management system allows this. If not, the shedding relay (FR: relais de délestage) will fully shut down the PV system when asked by the grid operator. It seems that this is currently not used neither by CREOS.

Smarty can also limit charging power of night storage heaters (DE: Nachspeicherheizung, FR: chauffage à accumulation) systems through shedding relays.

Billing

Every 15 minutes, the Smarty transmits to the grid operator (or the third party data collection partner) the average electricity consumption of the last 15 minutes. This is used to bill the quantity of electrical energy that was consumed, as well as the monitoring of the load that you used on the grid.

As of 1st January 2025, every household in Luxembourg is categorised in a power consumption category. Most households are in category 3 kW. Households with electric cars or heat pumps might be categorised into 7 kW or 12 kW categories. The monthly costs depend on the power consumption class that you are in.

If you are in the 3 kW category, and you drag 5 kW from the grid for about an hour, you have to pay additional fees (FR: supplément pour le dépassement), in this case: 5-3=2 kWh, which currently equals to 2*0.1139€ = 0,2278€.

If you use for example a (tea) water heating device that pushes your maximal power consumption over the 3 kW limit for about a minute, the fact that Smarty calculates the average over 15 minutes should assure that this short stepping over your category’s boundaries should not be visible on your invoice.

https://www.creos-net.lu/fileadmin/dokumente/downloads/fr_tarif_electricite_2025.pdf

To find out what your power category is, check your electricity provider’s portal, like myenovos.eu.

Balancing out consumption and production on 3-phase grids

When you are running a balcony power plant (DE: Balkonkraftwerk), you might be interested in how the Smarty manages billing or settlements of electricity production over the three phases. In Luxembourg, a normal household is connected to the power grid on 3 phases each capable of pumping 40A of current.

When you connect such a small photovoltaic system to a power socket on your balcony, it will feed the produced electricity only to the phase where you connected it. But this might not be the phase where your house is consuming most electricity, so you are feeding your produced electricity to the grid and drag the electricity from the grid that you need on the other phase (buying from thew grid is much more expensive then what you get paid for injecting electricity).

But there is good news: the smarty will balance this out (DE: saldierender Stromzähler), as it sums up power consumption on all three phases and power production on all three phases and only considers the difference for billing. Without this, a (mono-phase) balcony power plant is not really interesting for private use.

[Unfortunately I only have oral confirmation of this from an engineer. No guarantee on this information. It might also change over time.]

Monitoring your consumption and production

You have three possibilities to extract consumption and production data from your Smarty smart meter.

(1) By default, Smarty shows the most important values in a 5 seconds rhythm. Additionally: With the green button on your Smarty, you can cycle through the data that the meter gathers. To understand what value is currently displayed, Smarty adds the OBIS code to the display.

OBIS codes

(2) Plug the Smarty+ dongle into your Smarty meter’s P1 port and monitor your data on your smartphone

(3) Connect the P1 port over a serial / USB to a computer and monitor your data using a home automation software like for example openHAB with the help of the DSMR binding.

When using the P1 port, solution 2 & 3, you need to ask the grid operator to give you the P1 decryption key.

Source for most of the information above: https://www.creos-net.lu/en/individuals/practical-info/faq

Some information was confirmed in a telephone discussion with a grid engineer in January 2025. The general disclaimer applies here too, of course!

Proxmox rotating network interfaces

It sometimes happens that when I shutdown Proxmox for some hours, that after restart, the network interface does not work. I figured that Proxmox is rotating the network interface numbers which leads to the Linux network bridges to not map to the correct network interface and thus leads to no network connectivity. The only thing that helps is to adapt the /etc/network/interfaces file.

Some commands that can help:

brctl show
ifconfig vmbr0
dmesg
lspci | grep -i ethernet

Increase size of disk partition in Ubuntu

If you try to upgrade your system and you lack disk space, here is a solution that might help.

Run “df -h” to check how much space your system is using.

df -h

If it is at 100%, check if the partition still has some spare space left to be used.

lsblk

If the size of the partition your Ubuntu system is living on (f.ex. sda3) is bigger thant the allocated space for “ubuntu–vg-ubuntu–lv”, then you can add space to the system partition.

Result of lsblk (15GB remaining to be added)

You need to extend the logical volume to the maximum free space available:

sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv

Then you need to expand the filesystem:

sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

Check with df -h ig the additional space has been allocated.

Optimising warm water management with solar thermal energy

Out of the box, a lot of solar thermal energy installations are not really functioning in a transparent way. It is quite difficult to really know what the installation is doing and how much energy it adds into the warm water reservoir. Our installation, a Buderus gas condensing boiler system (Logamax plus GB162-25 V3) that manages our solar thermal panels, does give some information on the output of the system, but it does not have a unit, thus the numbers are not really directly interpretable, besides seeing if you produced more, less or a lot less/more energy through the solar thermal panels compared to the previous days.

Solar thermal installation’s added energy to the warm water system

After 10 years, our solar thermal installation failed to function for an undefined period of time (we don’t know how the system really failed), including at least one summer. The result was that the solar liquid became very viscous as during the summer months it overheated and boiled in the panels. The result was partially blocked hoses carrying the solar liquid from the roof to the heating system in the basement. Fortunately, some minor flow still was assured so that we could flush the whole system and get rid of most of the solidified liquid that was blocking the hoses. A faulty pump was the reason for this failure.

The Buderus management system showed that the pump was working. What I did not know was, that the display only indicated that it had sent electricity to the pump. It did not get any reliable feedback from the pump indicating if the pump really pumps. So technically, the user is blind and does not really know if the pump is really working or not. After flushing the hoses and panels, as well as installing a new pump, the system was refilled with fresh solar fluid and luckily the system came back to life.

I was then looking for a monitoring solution and find this device from a dutch enthusiast. Its EMS-ESP software helps me render the information from my heating system, including solar thermal panels, visible in my home automation system.

From there started a journey to understand how the heating system works and figuring out how to set it up correctly. To optimise the system, maximise the collection of heat from the sun and minimise gas consumption and heat losses, I worked on the following variables:

  • I augmented the maximal temperature of the warm water, when heated by the solar thermal system. I went up to 80°C (beware that such a high temperature also has side-effects, mainly on the longevity of the appliance). Be careful that your warm water recipient does not feed very hot water into your pipes. The risk to burn yourself, especially for kids, is extremely high. Make sure to have a mixing tap installed (at the exit of your warm water appliance and before your pipe system) that only lets water flow to your house system with a pre-defined maximal temperature, for example 50°C.
  • I reduced the temperature where the gas condensing fills in to heat up the warm water. The lower you can go in temperature, and still have enough warm water at your disposal, the better it is, as cou can use more energy from the sun. Ideally, the boiler is at 45°C each morning so that I have enough reserve to add a lot of heat to it. My gas boiler jumps in when warm water temperature drops to 45°C.
  • I configured the warm water circulation pump so that it only kicks in, when imminent warm water demands from a remote location in the house happen. The circulation pump starts when the light in the shower is witched on. We just have to wait for half a minute to make sure warm water is available in the water collector next to the shower. Having the circulation pump run all day, “extracts” the heat from your warm water reservoir. Without any circulation, my warm water reservoir looses between 0.5 and 0.7°C per hour.
  • To avoid excessive gas consumption, I completely switch off the heating circuit of the gas system during summer months (I only keep the warm water circuit switched onI). Our system happens to consume between 1 and 1.5 cubic-metre of gas even if all heating appliances are shut off an d the outside thermostat communicates to the system that there is no heating needed. This might by a configuration issue, but until now I could not find an issue in the configuration parameters.