Saturday, 31. May 2025 Week 22
As part of my migration to Bunny CDN for andreas-jaggi.ch, I also moved the DNS zone over.
There are not many records in the zone, but one which turned out to be a bit more tricky was the CAA one.
I wanted to use the following Terraform snippet to create it:
resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
zone = bunnynet_dns_zone.andreas_jaggi_ch.id
name = ""
type = "CAA"
value = "0 issue \"letsencrypt.org;validationmethods=http-01\""
}
But this always failed with a cryptic error message during terraform apply
:
│ Error: Unable to create DNS record
│
│ with bunnynet_dns_record.andreas_jaggi_ch_CAA,
│ on dns.tf line 22, in resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA":
│ 22: resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
│
│ A tag can be a maximum of 50 ASCII characters.
After some head-scratching I figured out that the Terraform provider has dedicated fields for the flags
and tag
parts of the CAA DNS record.
And it insists on them being used this way:
resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
zone = bunnynet_dns_zone.andreas_jaggi_ch.id
name = ""
type = "CAA"
tag = "issue"
flags = 0
value = "letsencrypt.org;validationmethods=http-01"
}
With this in place, it worked fine.
And it prepared me also for the MX record where a similar approach is required.
For andreas-jaggi.ch I wanted to try out Bunny CDN.
Everything went very smooth and I nicely used Terraform to configure Edge Rules blocking all unwanted access.
As andreas-jaggi.ch does not have much content this resulted in a list of allowed files similar to this:
resource "bunnynet_pullzone_edgerule" "andreas_jaggi_4" {
pullzone = bunnynet_pullzone.andreas_jaggi.id
description = "block not(known good) http://www.andreas-jaggi.ch"
enabled = true
match_type = "MatchNone"
actions = [{ type = "BlockRequest", parameter1 = null, parameter2 = null, parameter3 = null }]
triggers = [
{
match_type = "MatchAny",
patterns = [
"http://www.andreas-jaggi.ch/",
"http://www.andreas-jaggi.ch/favicon.ico",
"http://www.andreas-jaggi.ch/robots.txt",
"http://www.andreas-jaggi.ch/security.txt",
],
type = "Url", parameter1 = null, parameter2 = null
},
]
}
After this I did setup the www subdomain as CNAME and added it as additional hostname to the CDN Pullzone.
But the process to get a Let's Encrypt certificate for the www subdomain always failed with an error.
This is where I messed up.
Turns out my Edge Rules blocking all unwanted access also blocked the Let's Encrypt validation requests. 🤦
Once I realized this (which took a shamefully long amount of time), I added an entry to the Edge Rules for the .well-known/acme-challenge/
subpath:
resource "bunnynet_pullzone_edgerule" "andreas_jaggi_4" {
pullzone = bunnynet_pullzone.andreas_jaggi.id
description = "block not(known good) https://www.andreas-jaggi.ch"
enabled = true
match_type = "MatchNone"
actions = [{ type = "BlockRequest", parameter1 = null, parameter2 = null, parameter3 = null }]
triggers = [
{
match_type = "MatchAny",
patterns = [
"http://www.andreas-jaggi.ch/",
"http://www.andreas-jaggi.ch/favicon.ico",
"http://www.andreas-jaggi.ch/robots.txt",
"http://www.andreas-jaggi.ch/security.txt",
"http://www.andreas-jaggi.ch/.well-known/acme-challenge/*",
],
type = "Url", parameter1 = null, parameter2 = null
},
]
}
With this in place, the process worked immediately and the www subdomain now also serves encrypted traffic. 🔐
Thursday, 29. May 2025 Week 22
In the You can style alt text like any other text article, Andy Bell showcases how the alt test of images can be styled with CSS.
And then goes on to provide a smooth gracefully degrading experience by leveraging JavaScript to style image loading errors differently than successfully loaded images.
I like that CSS gives us plenty of opportunities to add finer details when we want them.
One of those finer details is making the experience of an image not loading a little better.
The alt
text is surfaced on the page when an image fails to load.
It’s yet another reason to write good alt text and really hone your skills in creating a user-focused experience by being descriptive.
What’s happening here is I’m hooking a function up to the <img>
’s built-in onerror
event.
When that event fires, I’m setting a data-img-loading-error
attribute.
(via)
Tuesday, 27. May 2025 Week 22
Here comes a handy utility: timeout
.
As the name suggests, this command adds a timeout to other commands.
You specify the time limit you want to wait for a command and if that time passes,
timeout
sends a signal to terminate it and exits with non-zero.
By default, timeout
sends SIGTERM
,
but you can change it with the --signal
flag, e.g. timeout --signal=SIGKILL 1s foo
.
For example, timeout 1s sleep 5
will send the SIGTERM
signal to sleep
after 1 second.
(via)
Sunday, 25. May 2025 Week 21
After a bit more than 13 years I now removed Disqus from the blog.
Over these years, it only contributed 36 comments.
Most of them around a single post that made it onto the frontpage of Hacker News and Reddit.
I'll extract these comments from the export file of Disqus.
Then backfill them as static entries to the corresponding posts so they are preserved.
In the future I might add another way to comment/contribute directly on the blog.
For now, please use the communication channels listed on the About page.
(via)
Sunday, 18. May 2025 Week 20
This year, I missed the CSS Naked Day.
I very much like the idea of ensuring my blog is also readable without CSS.
Thus to make sure I'll not miss it next year, I've added the following snippet into my nginx config:
set $csp_style "'unsafe-inline' blog.x-way.org https://*.disquscdn.com https://gist.x-way.org/assets/";
if ( $time_iso8601 ~* ^20[0-9][0-9]-0?4-0?9T.*$ ) {
set $csp_style "'none'";
}
include /etc/nginx/conf_includes/x-way.org_security_headers;
It's using a very basic mechanism to ensure no CSS is shown on April 9 next year (or any other year)
First it checks if the date is April 9, and if yes it sets the variable for the style-src
Content-Security-Policy header to 'none'
.
Which forbids the browser to load any CSS when rendering my blog.
Now let's see how it looks next April 9th ☺️
Tuesday, 13. May 2025 Week 20
Being only an occasional coffee drinker, I lack any coffee making machinery at home. Not even some instant-coffee.
Thus this text is going to be less extensive than the inspiration post from Mike.
I have my coffee usually outside. Sometimes a cup in the office at work (when not working from home).
And quite often in one of the nice cafes in Zurich.
This afternoon I had a very yummy Flat White made from «UBA purple» beans at MAME 😋
Sunday, 11. May 2025 Week 19
In the Passkeys for Normal People article, Troy Hunt explains in simple terms how Passkeys work.
The article covers also what type of attack they prevent against (using Troy Hunt's recent falling for phishing experience) and how to setup Passkeys for common services.
At the end it briefly touches on using hardware U2F keys as well 🔐
Added some subtle links to the next and previous posts.
Turns out this is a built-in feature of Jekyll, which I never thought about using so far :-)
Here's the snippet which I just added to my custom post layout template:
<footer class="navfooter">
<nav>
{%- if page.next.url %}
<a rel="next" href="{{ site.url }}{{ page.next.url }}" title="{{ page.next.title | strip_html | escape_once }}">←</a>
{% endif -%}
{%- if page.previous.url %}
<a rel="prev" href="{{ site.url }}{{ page.previous.url }}" title="{{ page.previous.title | strip_html | escape_once }}">→</a>
{% endif -%}
</nav>
</footer>
(Only thing left now, is to tame the ugly Disqus comments section so these links become more visible…)