Jae's Blog

Verkkokauppa.com DNS

Verkkokauppa.com is a chain of web and physical stores originating from (and limited to) Finland, akin of Amazon here if you will.

When trying to search for a USB-C computer mouse, IPvfoo told me that the website was accessible over IPv6, which I never noticed before.

I then queried the DNS server to see where it was hosted and:

Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
Name:	verkkokauppa.com
Address: 34.95.73.242Code language: CSS (css)

Weird, no v6 there, let’s try with the www subdomain now:

Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
www.verkkokauppa.com	canonical name = www.verkkokauppa.com.cdn.cloudflare.net.
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 104.18.33.183
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 172.64.154.73
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 2606:4700:4400::ac40:9a49
Name:	www.verkkokauppa.com.cdn.cloudflare.net
Address: 2606:4700:4400::6812:21b7Code language: PHP (php)

Turns out the www subdomain (which the apex redirects to) is proxied through CloudFlare, therefore offering IPv6 connectivity, and uses different nameservers.

The apex uses Netnod DNS (AS8674)

; <<>> DiG 9.20.4 <<>> NS verkkokauppa.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35173
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;verkkokauppa.com.		IN	NS

;; ANSWER SECTION:
verkkokauppa.com.	2986	IN	NS	nsu.dnsnode.net.
verkkokauppa.com.	2986	IN	NS	ns2.verkkokauppa.com.
verkkokauppa.com.	2986	IN	NS	nordic1.dnsnode.net.
verkkokauppa.com.	2986	IN	NS	ns1.verkkokauppa.com.
verkkokauppa.com.	2986	IN	NS	nsp.dnsnode.net.

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Sun Dec 29 17:41:39 EET 2024
;; MSG SIZE  rcvd: 150Code language: CSS (css)

The www subdomain uses CloudFlare (AS13335)

; <<>> DiG 9.20.4 <<>> NS www.verkkokauppa.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41779
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;www.verkkokauppa.com.		IN	NS

;; ANSWER SECTION:
www.verkkokauppa.com.	1020	IN	CNAME	www.verkkokauppa.com.cdn.cloudflare.net.

;; AUTHORITY SECTION:
cloudflare.net.		1800	IN	SOA	ns1.cloudflare.net. dns.cloudflare.com. 2359389931 10000 2400 604800 1800

;; Query time: 16 msec
;; SERVER: 192.168.1.1#53(192.168.1.1) (UDP)
;; WHEN: Sun Dec 29 17:42:04 EET 2024
;; MSG SIZE  rcvd: 157Code language: CSS (css)

Now, back to finding my USB-C mouse.

A week of Framework

As per my usual tradition when I receive a new cool device, I have to write about it a week later, then either three months or a year later (as I did previously with my Index, or work MacBook Pro M1).

As you may know, my previous laptop was a ThinkPad x200.
It’s not exactly a young machine, being around 16 years old now.

As I started working on more demanding projects (mainly C# ones), the x200 simply wasn’t enough (it couldn’t even run a modern web browser any more).

This is why I decided to scout for a new laptop.

Fear not, the x200 is not going to waste! It will now be used mainly to test Libreboot and other stuff like that.

Now, I had a bunch of criteria for the new laptop:

  • Can last as long as the x200
  • Can run my IDEs correctly (namely Sublime Text and JetBrains Rider)
  • Has a proper GPU (to run VR stuff)
  • Has modern hardware in general

The Framework 16, tho expensive, checked a lot of those cases:

  • Can last long by sheer virtue of being repairable
  • Has modern hardware, therefore can run my IDEs correctly
  • Can be upgraded to have a dedicated GPU

So for around €2100 (ouch, my wallet D:), this is what you get with Framework:

  • AMD Ryzen 7 7840HS (8c16t)
  • Radeon 780M Graphics integrated graphics (decided to buy the GPU upgrade later)
  • 32GB of RAM (single stick to leave room for upgrades)
  • Wi-Fi 6
  • A 2560×1600@165hz display
  • 2x USB-C expansion cards
  • 2x USB-A expansion cards
  • 1x HDMI expansion card
  • 1x DisplayPort expansion card
  • 1TB of m.2 storage
  • Power supply
  • Keypad module

Overall, pretty good specs by my standards for a laptop.
Before you say anything: the HDMI is for a second screen, the DisplayPort is for VR headsets.

To save up some money, I also decided to take it as the DIY edition without any OS, and then install Fedora on it.
The laptop itself was painless to build, even fun. The only issue is my hands trembling when doing anything requiring a bit of precision (in this instance handling a screwdriver with small screws), but that’s a me issue.

There was a small issue on first boots where the keyboard wasn’t responding at all, but taking it apart and verifying all the connections one by one made it work.

Fedora is one of the supported OSes on the Framework, along Ubuntu and Windows. I would have gone with Arch, however I wanted a headache-free setup this time, which Fedora offered.

During this week we actually got a new BIOS upgrade for the 16 being 3.05, fixing some security issues and adding a cool new feature to extend the battery longevity.

Upgrading the BIOS was pretty much painless thanks to fwupdmgr and was easy as:

$ fwupdmgr refresh --force
$ fwupdmgr get-updates
$ fwupdmgr updateCode language: Bash (bash)

Then, being patient.
I remember having to fiddle with USB keys back a few years ago, so this CLI utility is much welcome.

The battery life itself is correct, never really running out when working on stuff.

Fedora itself also is a breeze to use, having GUIs for everything simplifying the task a lot.
I do miss a bit my good old XFCE4, but GNOME does the job just fine as well.

Another thing I totally forgot to do after the first install was to get the EasyEffects profile, which does makes a huge difference on the laptop’s audio.

Overall, I’m very satisfied with what I got, remains to see a few things:

  • Will new hardware upgrades come out (for instance, additional GPU modules)
  • Will any other companies start making expansion cards (instead of relying on Framework alone; though the community already made a lot of those)
  • Will Framework as a company remain in business long enough to offer the longevity I want

But those can only be answered with time. It goes without saying that most of the hardware replacement (or upgrades) (RAM, storage, etc) can be done with any off-the-shelf components and not just ones sold by Framework.

For now, I’ll keep using it, and I’ll see you peeps in either three months or a year (or both) if I don’t forget for the traditional update!

Deploying your own GitLab instance under 5 minutes

It’s no secret that I work around GitLab during my day job and that I generally love this software.
This blog post is therefore not biased at all in any way or form. (do I need to mark this further as sarcasm, or did everyone get the memo?)

For this quick tutorial, you’ll need:

  • Some machine where the instance will be hosted
  • Docker installed on the machine
  • Ability to read instructions

For this, we’ll be using docker compose which provides an easy way to bring services up from a configuration file.
This tutorial just provides a bare bones instance that you will need to configure further later.

Small note: for this to work, your system SSH daemon will need to run on something else than port 22.

The compose file for GitLab is really simple:

services:
  gitlab:
    image: gitlab/gitlab-ce:17.6.1-ce.0
    volumes:
      - ./gitlab/config:/etc/gitlab
      - ./gitlab/log:/var/log/gitlab
      - ./gitlab/data:/var/opt/gitlab
    ports:
      - "22:22"
      - "80:80"
      - "443:443"
Code language: YAML (yaml)

And there you go, name this file docker-compose.yml on your server and issue:

$ docker compose up -dCode language: Bash (bash)

After a few minutes, the GitLab instance should be reachable on the IP of your machine.

To reset the root password, use:

$ docker compose exec gitlab gitlab-rake "gitlab:password:reset[root]"Code language: Bash (bash)

Now, some few steps that are recommended to take after having a working instance:

  • Reverse-proxy GitLab and get HTTPS certificates for everything
  • Host a runner (to be able to utilize CI/CD)
  • Refine the Gitlab.rb configuration, most notably:

In a future blog post, I’ll show how to configure one cool feature of GitLab which is the service desk which can be useful for some projects.

Setting up WireGuard tunnels from a BGP router

I recently re-started my BGP shenanigans, and with that, re-setup some VPNs using WireGuard for my personal machines.

I basically use those to whitelist connections to certain applications to only the prefix used by my machines.

The host machine runs Debian and BIRD, and the end devices are diverse from standard Linux machines, to Windows desktops, to iOS devices.

First, the BIRD configuration is pretty trivial, just adding a route for the prefix via lo:

route 2a12:4946:9900:dead::/64 via "lo";Code language: PHP (php)

I’m aware my subnet configurations can be sub-optimal, but I’m just running this for fun, not for it to be perfect¨.

Then, generating WireGuard keys on the host (the package wireguard-tools will need to be installed):

$ umask 077
$ wg genkey > privatekey
$ wg pubkey < privatekey > publickeyCode language: Bash (bash)

Now, the WireGuard host configuration is pretty trivial:

[Interface]
Address = 2a12:4946:9900:dead::1/128
ListenPort = 1337
PrivateKey = myVeryPrivateKey=

The key generation on the client follows the same procedure, if not easier via a GUI. The configuration itself looks like this:

[Interface]
PrivateKey = myVerySecretKey=
Address = 2a12:4946:9900:dead::1337/128

[Peer]
PublicKey = serverPubKey=
AllowedIPs = ::/1, 8000::/1
Endpoint = [2a12:4946:9900:dead::1]:1337
PersistentKeepalive = 30
Code language: JavaScript (javascript)

Note that I’m using ::/1 and 8000::/1 in AllowedIPs on Windows as setting it to ::/0 kills IPv4 connectivity (that is sadly still needed) and local connectivity to stuff like my storage array. On Linux, ::/0 works as expected, letting IPv4 through correctly.

Now, we can add a Peer section into the server’s configuration:

[Peer]
# PC Client
PublicKey = clientPubKey=
AllowedIPs = 2a12:4946:9900:dead::1337/128
Code language: PHP (php)

Now you should be all set and ready to bring up the tunnel on both ends.

On the server (assuming your configuration file is named tunnels.conf):

$ systemctl enable wg-quick@tunnels
$ systemctl start wg-quick@tunnelsCode language: Bash (bash)

And on the client using the same procedure, or just clicking the “Connect” button on the GUI client.

I’ve had some cases where this all of this alone isn’t enough, and had to add the prefixes to lo.

For instance:

$ ip -6 add 2a12:4946:9900:dead::/64 dev lo

And in /etc/network/interfaces:

iface lo inet6 static
        address 2a12:4946:9900:dead::/64
Code language: JavaScript (javascript)

Tho I will admit, I had more issues setting this up than I should have, and most configs would benefit from being re-written. Admittedly, I executed and documented this procedure while being extremely tired, which of course causes some issues.

But at least, this works, and can be very useful when I’m connected to networks not offering IPv6 connectivity as well.

Sending commands to a Docker Compose Resonite headless

After searching for a bit, I found a way to send commands to a Resonite headless within Docker programmatically without having to run docker compose attach <container> and having to manually detach.

You will need the software socat installed on the host machine, given most of my machines are running Debian, this can be done via apt install socat.

Now, you can use:

echo 'worlds' | socat EXEC:"docker attach $(docker compose ps -q reso-headless)",pty STDINCode language: Bash (bash)

In this command, replace:

  • worlds by the command you want
  • reso-headless by the defined name of your headless container

Alternatively, you can just specify the container name directly instead of doing $(docker compose ps -q reso-headless).

Addendum:

For this to work, you will have to make sure your container is defined with:

    tty: true
    stdin_open: true
Code language: YAML (yaml)

in the Compose file.

Liberate your news with RSS

RSS, standing for Really Simple Syndication, is a really good and easy way to get all your news right onto your computer.

While the standard is fairly old now, being older than me, it still fills its purpose wonderfully.

To have a simple outlook, RSS allows you to get news from feeds made available by websites and aggregate them into a software.

Some good RSS readers include:

Finding RSS feeds is also easy. You can find them by searching online for them or looking for the RSS icon on websites (small dot with the three lines going out like a broadcast).

Some readers like RSSGuard also have a feature to discover RSS feeds on pages.

Some nice feeds I personally watch are:

  • Blender – https://blender.org/feed
  • Resonite – https://store.steampowered.com/feeds/news/app/2519830
  • Acrouzet (YouTube) – https://www.youtube.com/feeds/videos.xml?channel_id=UClv1kZDpIA9LcXPYY4KTU-w
  • The Servo blog – https://servo.org/blog/feed.xml
  • The Matrix blog – http://matrix.org/blog/feed/
  • Bellingcat – http://www.bellingcat.com/category/news/feed/rdf

Some tricks as well:

  • Any website using WordPress will have a feed at the URL /feed
  • You can watch updates for any Steam game or app using https://store.steampowered.com/feeds/news/app/<appid> (and replacing <appid> by the ID of the game which you can find in the store URL)
  • If a website doesn’t directly offers an RSS feed, you can build one by using something like rss-bridge or RSSHub
  • You can follow any YouTube channel using RSS by using https://www.youtube.com/feeds/videos.xml?channel_id=<channelid>
  • Most blogs also have a RSS feed (don’t forget to subscribe to this one to not miss anything in the future :3)

Overall, RSS is an amazing technology, supported by websites you wouldn’t even suspect.
I can only encourage using it as it’s lightweight, easy, ad-free (at least from the experience I’ve had from it).

I am not interested about AI

Recently, I’ve received some e-mails from so-called “AI” startups, wanting me to join them to develop their product.

I will be blunt: don’t bother. I’m not interested.

I’m not interested in your startup that resells the OpenAI API under a fancy interface.
I’m not interested in your startup that has no plans for the future beyond “we’ll see when we get more funding”.
I’m not interested in your startup that wastes incredible amounts of resources just to hallucinate results and for the whole thing to fall down in a year when the funding expires.

And once and foremost, I’m not interested in AI in general.

While I did thinker with it when it was new, it’s pretty much useless outside of making boilerplates.

Here, we make fresh, organic, handmade software.

Setting up a Resonite Headless and getting metrics from it

As you may know, I made a mod for the platform Resonite that allows you to export metrics from the game as OpenMetrics data readable by Prometheus amongst other software.

In this post, we’re gonna see:

  1. How to setup a basic Resonite headless on Windows
  2. How to install the Resonite mod loader
  3. How to install the Headless Prometheus Exporter mod
  4. How to install and configure Grafana and Prometheus to scrape the metrics

First, some pre-requisites:

Setting up a headless in Windows is really easy. To first get the headless files, there are two ways that all begin the same way, sending /headlessCode to the Resonite Bot while logged in-game. This will give you the code needed to activate the beta branch for the headless.

Now, to download the headless, you have two ways:

  1. Use the graphical Steam client
  2. Use SteamCMD

Using the Steam client is the easiest. Just right-click on Resonite, hit “Properties”, then “Betas”, enter the code you previously got into the field and click on “Check code”.
You should now be able to select the Headless branch in the small dropdown and will download it automatically to your Steam game folder.

When using SteamCMD, unpack the zip file it comes in in a directory, hold SHIFT and right-click, then select “Open PowerShell window here”. Once the PowerShell open, you can use the following command to download the headless (replace your account name, password and headless code):

.\steamcmd.exe +force_install_dir ./resonite +login <account name> <account password> +app_license_request 2519830 +app_update 2519830 -beta headless -betapassword <headless code> validate +quitCode language: Bash (bash)

You should now be able to find the headless within the resonite\Headless directory near where SteamCMD is unpacked.

Now, to run the mod itself, the headless is not enough, we need to extend it via the Resonite Mod Loader. Its installation is straightforward, as outlined by their README file:

Download ResoniteModLoader.dll to Resonite’s Libraries folder (C:\Program Files (x86)\Steam\steamapps\common\Resonite\Libraries).
You may need to create this folder if it’s missing.
Place 0Harmony.dll into a rml_libs folder under your Resonite install directory (C:\Program Files (x86)\Steam\steamapps\common\Resonite\rml_libs).
You will need to create this folder.
Add the following to Resonite’s launch options: -LoadAssembly Libraries/ResoniteModLoader.dll. If you put ResoniteModLoader.dll somewhere else you will need to change the path.
Optionally add mod DLL files to a rml_mods folder under your Resonite install directory (C:\Program Files (x86)\Steam\steamapps\common\Resonite\rml_mods).
You can create the folder if it’s missing, or launch Resonite once with ResoniteModLoader installed and it will be created automatically.
Start the game. If you want to verify that ResoniteModLoader is working you can check the Resonite logs. (C:\Program Files (x86)\Steam\steamapps\common\Resonite\Logs).
The modloader adds some very obvious logs on startup, and if they’re missing something has gone wrong. Here is an example log file where everything worked correctly.

Those same instructions also apply to the headless software. On certain Windows version, you might want to right click and open the properties of ResoniteModLoader.dll and 0Harmony.dll then check the “unblock” checkbox as it will prevent the mod loader from functioning correctly.

Once this is done, head over to the releases tab of the Headless Prometheus Exporter mod and download the HeadlessPrometheusExporter.dll file. You will need to move this file in the rml_mods folder that should be located in the headless directory. If this folder isn’t there, you can create it. Also check the properties of this dll as well for the unblock checkmark.

Now that the mod installation is done, we have one last step: configuring our headless. This step is also incredibly easy, being documented on the Resonite Wiki.
I can recommend going in the Config folder then copying the example configuration file provided to fit your needs. It is not recommended to start out with a “minimal” configuration file, that might lack some essential settings and result in the headless not working as intended or not starting at all. Once you are familiar with what goes where and does what, feel free to trim the configuration file following your needs.

If you still have your PowerShell window open, you can type cd resonite\Headless to navigate to where the executable is and then use the following to start it with the mods:

.\Resonite.exe -LoadAssembly Libraries\ResoniteModLoader.dllCode language: Bash (bash)

After waiting a bit for it to start, you should be able to visit https://localhost:9000 in a web browser and see some metrics being displayed such as some world stats, engine FPS and many others.

Now we can tackle the last issue: how to display those metrics. For this, we’re going to use Prometheus in combination with Grafana, which are hands-on probably the best solution for this.

We’re gonna use my pre-made minimal Grafana setup for this. You can obtain the files by either using git clone https://g.j4.lc/general-stuff/configuration/grafana-minimal-setup.git or by downloading a ZIP or tarball of the source.

Getting started with it is extremely easy, but first, let’s go through each configuration file and what it does.

First, let’s open prometheus/prometheus.yml:

scrape_configs:
  - job_name: 'resonite-headless'
    scrape_interval: 15s
    static_configs:
      - targets: ['host.docker.internal:9000']
Code language: YAML (yaml)

This one is fairly simple. This configures Prometheus, whose job is to aggregate the metrics the mod is exposing. In this particular configuration, we are telling it to scrape our headless at the address host.docker.internal:9000 every 15 seconds.

Note that in most cases, you would need to use localhost:9000 or container_name:9000; host.docker.internal is only used because the headless is not in a container and on the host machine.

This leads us to our docker-compose.yml:

services:
    grafana:
        image: grafana/grafana
        ports:
            - 3000:3000
        volumes:
            - ./grafana:/var/lib/grafana

    prometheus:
        image: prom/prometheus
        volumes:
            - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
            - ./prometheus/data:/prometheus
        command:
            - '--config.file=/etc/prometheus/prometheus.yml'
            - '--storage.tsdb.path=/prometheus'
            - '--log.level=debug'
        extra_hosts:
            - 'host.docker.internal:host-gateway'
Code language: YAML (yaml)

It basically defines two services:

  • Grafana, which:
    ; Will store all of its data in the directory ./grafana
    ; Will be accessible on the port 3000
  • Prometheus, which:
    ; Will store all of its data in the directory ./prometheus/data
    ; Have access to the configuration file mentioned earlier
    ; Pass the localhost of the machine inside (as well as passing some command-line configuration arguments defining the configuration file path and the storage path)

Now that this is out of the way, open a PowerShell window in the directory where the docker-compose.yml file is located and make sure Docker is launched. There, just write docker-compose up -d and watch Docker automatically pull and start the images. If you are curious, you can use docker compose logs -f to see all the logs in real time.

After waiting for a minute or two, you can visit http://localhost:3000 in a web browser to setup Grafana. The default login should be admin and admin for both user and password.

Once in Grafana, head to the menu on the left, go in “Connections” then “Data sources”. There, select “Add new data source” and select Prometheus. You will only need to set the URL of said source to http://prometheus:9090, you can then go on the bottom and click on “Save & Test”.

You can now either select to go to the explore view or create a brand new dashboard on the top right of the screen. I can recommend playing with the Explore view for a bit before starting to build dashboards, as it will teach you the different types of visualisation as well as some useful queries.

Some metrics you can query include:

# RESONITE HEADLESS PROMETHEUS EXPORTER
totalPlayers 1
totalWorlds 1
# WORLD STATS 
world_users{label="S-09444920-caab-4c3d-a242-a50b028c33e6"} 1
world_maxusers{label="S-09444920-caab-4c3d-a242-a50b028c33e6"} 16
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalCorrections"} 82
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalProcessedMessages"} 4049
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedConfirmations"} 0
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedControls"} 3
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedDeltas"} 554
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedFulls"} 0
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalReceivedStreams"} 3491
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentConfirmations"} 554
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentDeltas"} 2410
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentControls"} 3
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentFulls"} 1
world_network{label="S-09444920-caab-4c3d-a242-a50b028c33e6",type="totalSentStreams"} 106
engineFps 59.98274
completedGatherJobs 0
startedGatherJobs 0
failedGatherJobs 0
engineUpdateTime 0.0004873Code language: PHP (php)

Noting that the S-09444920-caab-4c3d-a242-a50b028c33e6 is not a set variable as it is a session ID.

Now that you’ve done all of this, you can now enjoy having a nice dashboard displaying metrics about your headless!

You can also expand on the subject by reading further:

Newer Posts
Jae 2012-2025, CC BY-SA 4.0 unless stated otherwise.