Connecting an existing FreshRSS instance to OIDC

Specifically, how to do so without starting over.

WARNING: I am not responsible for you losing data by doing this. Make sure you have a recent backup before you go digging around and changing things in the filesystem.

Overview

It came to my attention yesterday that the FreshRSS project directly supports OIDC now.

I’ve been using the Traefik ForwardAuth middleware to get the same functionality for some time, but this allows me to get rid of a layer.

It was also slightly clunky, since after I’d logged in through Authentik, I’d have to also authenticate via FreshRSS. I’m aware I could have simply disabled authentication, but I chose not to at the time.

I had been using the LinuxServer.io Docker container, but this functionality depended on Apache’s OIDC module, so it was unsupported. (The LinuxServer.io container uses nginx instead of Apache.)

OIDC Setup

The first thing I needed to do was switch to the official container on DockerHub.

The volume layout was a bit different from the LinuxServer container, so I had to stop the container and move some files around.

All you should need to do is move the data and extensions folders out from under the config directory that the LSIO container uses, and make them their own separate bind mounts.

Next, I set about configuring OIDC.

I followed their official instructions to set up OIDC with Authentik.

This was mostly fine, though I had to make a few tweaks to their environment variables. The OIDC_SCOPES variable in particular was my issue - it would not work if I had it in quotes. I can only assume that this was being interpreted as a single string by the application.

Here’s what my Docker Compose file wound up looking like at the end. The OIDC values have been replaced with gibberish, make sure to substitute your own if you copy and paste.

services:
  freshrss:
    container_name: freshrss
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
      - TRUSTED_PROXY=172.23.0.0/16
      - OIDC_ENABLED=1
      - OIDC_PROVIDER_METADATA_URL="https://auth.example.com/application/o/fresh-rss-oidc/.well-known/openid-configuration"
      - OIDC_REMOTE_USER_CLAIM=preferred_username
      - OIDC_CLIENT_ID=1234455262233
      - OIDC_CLIENT_SECRET=7dfadkfjakldgjad7897324
      - OIDC_CLIENT_CRYPTO_KEY=dfajfakjglkdg70708723434
      - OIDC_SCOPES=openid email profile
      - OIDC_X_FORWARDED_HEADERS=X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto
    volumes:
      - ./data:/var/www/FreshRSS/data
      - ./extensions:/var/www/FreshRSS/extensions
    restart: unless-stopped
    image: "freshrss/freshrss:latest"
    labels:
      - traefik.enable=true
      - traefik.http.routers.freshrss-external.rule=Host(`rss.example.com`)
      - traefik.http.routers.freshrss-external.entrypoints=https
      - traefik.http.routers.freshrss-external.middlewares=default-headers@file
      - traefik.http.routers.freshrss-external.tls=true
      - traefik.http.routers.freshrss-external.service=freshrss-external
      - traefik.http.services.freshrss-external.loadbalancer.server.port=80
    networks:
      - proxy

networks:
  proxy:
    external: true

I’m not 100% certain that the TRUSTED_PROXY setting is necessary. The subnet there is the subnet used by my proxy docker network. It works and I’m too lazy to try removing it.

You can get this subnet by running docker network inspect <network-name>

> docker-ubuntu in ~/docker/freshrss/data
docker network inspect proxy
docker network inspect proxy
[
    {
        "Name": "proxy",
        "Id": "4b6ea83029c955c6f7609af4552ed053760aadbb6de5c72f8390d3cfa96023b1",
        "Created": "2022-09-24T02:01:57.289063399Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/16", <-------- This
                    "Gateway": "172.23.0.1"
                }
            ]
        },

<snip>

Not losing data

Onto the meat and potatoes.

In order to not log into a completely fresh profile with no articles and none of your settings - your user ID in FreshRSS needs to match that of your identity provider.

Mine did not, so I had to do a little detective work.

I stopped the container with a docker compose down and started poking about in the filesystem, trying to figure out how I could change my username.

As it turns out, it’s quite simple - each user has their own directory under data/users/, and each user has their own individual sqlite database. I poked around in the database a little, but there didn’t seem to be any indication of who owned each entry it stored in its tables.

This led me to believe that maybe FreshRSS was just reading the directory structure to find its list of users.

docker-ubuntu in ~/docker/freshrss/data
> tree users
users
├── MyUsername
│   ├── config.php
│   ├── db.sqlite
│   └── log.txt
├── _
│   ├── db.sqlite
│   ├── index.html
│   ├── log.txt
│   ├── log_api.txt
│   └── log_pshb.txt
└── index.html

2 directories, 10 files

So as an experiment, I simply renamed the user folder under data/users to match the one from my identity provider and started FreshRSS back up. Imagine my surprise when that simply worked.

I was logged in automatically with Authentik SSO, and I had all of my feeds, saved articles and settings.