Compare commits

...

32 commits

Author SHA1 Message Date
d31c4cafdb Merge branch 'main' into preview 2024-01-15 14:36:21 -06:00
35b2791dce rss: strip <svg>...</svg> elements 2024-01-15 14:33:26 -06:00
f2f2c5aa9f fix broken link in post 2024-01-15 14:10:19 -06:00
John Bowdre
841767dd66
Merge pull request #4 from jbowdre/preview
RSS improvements
2024-01-15 14:07:05 -06:00
3e7c2ddbe8 remove minify from netlify config 2024-01-15 14:04:59 -06:00
cdc261b82e enable minify in config, don't minify xml 2024-01-15 14:00:28 -06:00
9a8c130c63 Revert "rss: don't minify whitespace in xml"
This reverts commit b5475f0574.
2024-01-15 13:59:40 -06:00
b5475f0574 rss: don't minify whitespace in xml 2024-01-15 13:53:52 -06:00
39079ebde1 rss: alternate approach to image embeds 2024-01-15 13:39:27 -06:00
cec57e09d1 rss: fix image embeds 2024-01-15 13:23:34 -06:00
7497882d94 rss: encode content in description 2024-01-15 13:09:20 -06:00
f98628bfce Revert "rss: highlight xml"
This reverts commit 9ad14f2092.
2024-01-15 12:57:07 -06:00
9ad14f2092 rss: highlight xml 2024-01-15 12:51:22 -06:00
094fa7ec1a rss: use full text instead of summary 2024-01-15 12:42:20 -06:00
8dfff8f099 rename and future-date post 2024-01-14 21:22:24 -06:00
16880cc516 make --off-fg a little lighter to improve code/pre contrast 2024-01-14 21:20:45 -06:00
56155058b0 new post: securing-web-servers-cloudflare-tunnel 2024-01-14 20:45:54 -06:00
8950a2bf1d minor contrast tweak 2024-01-13 17:20:33 -06:00
006eb3ce9c fix for faded statuslol time/respond links 2024-01-13 17:12:37 -06:00
128b9f57e7 update copyright message 2024-01-10 16:21:36 -06:00
922366762d update omg links 2024-01-08 15:06:34 -06:00
bd7751e0ca new year, new copyright 2024-01-02 07:28:09 -06:00
dc6939de2d automatically restart containers unless stopped 2024-01-01 16:41:39 -06:00
f3161654d3 Merge branch 'giscus' 2023-12-31 16:17:09 -06:00
6bbcfded0f fixes for loading giscus comments 2023-12-31 16:16:02 -06:00
df54406ef6 fix comment flag in frontmatter 2023-12-31 16:03:22 -06:00
59353bf3ca fix broken comments 2023-12-31 15:59:33 -06:00
6d5355541b initial work on giscus setup 2023-12-31 15:29:58 -06:00
36dd55a821 update gitignore 2023-12-31 15:06:52 -06:00
7fba1865a0 more PURLs 2023-12-30 20:47:38 -06:00
11093e5c98 use l.runtimeterror.dev for PURLs 2023-12-30 20:44:32 -06:00
John Bowdre
dd5c13ecae
Merge pull request #3 from jbowdre/preview
updates to rss handling
2023-12-30 17:14:39 -06:00
57 changed files with 301 additions and 86 deletions

2
.gitignore vendored
View file

@ -4,5 +4,5 @@
/package.json
/public/
/resources/
/.env
/.env*

View file

@ -6,7 +6,7 @@ draft: true
description: "This is a new post about..."
featured: false
toc: true
comment: true
comments: true
series: Tips # Projects, Code
tags:
- 3dprinting

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Quick script to run local builds
source .env
hugo --minify --environment local -D
hugo --environment local -D
npx torchlight
python3 -m http.server --directory public 1313

View file

@ -1,7 +1,7 @@
baseURL = "https://runtimeterror.dev"
theme = "risotto"
title = "runtimeterror"
copyright = "© 2023 runtimeterror"
copyright = "© 2024 John Bowdre"
paginate = 10
languageCode = "en"
DefaultContentLanguage = "en"
@ -60,3 +60,6 @@ enableInlineShortcodes = true
tag = "tags"
series = "series"
[minify]
disableXML = true
minifyOutput = true

View file

@ -8,19 +8,29 @@ numberOfRelatedPosts = 5
indexTitle = ".-. ..- -. - .. -- . - . .-. .-. --- .-."
[author]
name = "John Bowdre"
email = "jbowdre@omg.lol"
username = "jbowdre"
# Comments
comments = true
utterancesRepo = "jbowdre/site-comments"
utterancesIssueTerm = "og:title"
utterancesTheme = "gruvbox-dark"
giscusCategory = "Announcements"
giscusCategoryId = "DIC_kwDOKKEGD84CcG89"
giscusCrossOrigin = "anonymous"
giscusEmitMetadata = "0"
giscusInputPosition = "bottom"
giscusLang = "en"
giscusLoading = "lazy"
giscusMapping = "og:title"
giscusReactions = "1"
giscusRepo = "jbowdre/site-comments"
giscusRepoId = "R_kgDOKKEGDw"
giscusStrict = "0"
giscusTheme = "noborder_gray"
analytics = true
[author]
name = "John Bowdre"
email = "jbowdre@omg.lol"
username = "jbowdre"
[theme]
palette = "runtimeterror"
@ -139,7 +149,7 @@ url = "https://social.lol/@jbowdre"
[[socialLinks]]
icon = "fa-solid fa-heart"
title = "omg.lol"
url = "https://jbowdre.omg.lol"
url = "https://jbowdre.lol"
[[socialLinks]]
icon = "fa-solid fa-comments"

View file

@ -1,2 +1,2 @@
comments = false
comments = true
analytics = false

View file

@ -17,21 +17,21 @@ I'm now part of a small platform engineering team within that same large corpora
On my off time, I tinker with new [projects](/series/projects) in my little homelab (and share some of those adventures here). I also help out on Google's product support forums as a [Product Expert](https://productexperts.withgoogle.com/what-it-is), where I support Pixel phones, earbuds, and watches, as well as Chromebooks (primarily with Linux-related queries). Helping users troubleshoot their issues scratches my problem-solving itch, and it keeps me connected with some really great like-minded tech enthusiasts.
On weekends, I race my daily-driven 2014 Subaru BRZ in local [autocross events](https://www.youtube.com/playlist?list=PLwzr4uKY-x-EwCv-rWNGefdikuW6Oy9O_) or wrench on my 1974 Volkswagen Karmann Ghia.
On weekends, I race my daily-driven 2014 Subaru BRZ in local [autocross events](https://l.runtimeterror.dev/my-autox-vids) or wrench on my 1974 Volkswagen Karmann Ghia.
And in the free time I have left, I game on my Steam Deck.
See what I've been up to on:
- [GitHub](https://github.com/jbowdre)
- [CounterSocial](https://counter.social/@john_b)
- [status.lol](https://status.lol/jbowdre)
- [/now](https://jbowdre.omg.lol/now)
- [status.lol](https://status.jbowdre.lol)
- [/now](https://now.jbowdre.lol)
Connect with me via:
- [SimpleX Chat](/simplex/)
- [Matrix](https://matrix.to/#/@jbowdre:omg.lol)
- [Electronic Mail](mailto:jbowdre@omg.lol)
- [PGP: 613F B70C 4FA7 A077](https://home.omg.lol/keychain/jbowdre/pgp)
- [PGP: 613F B70C 4FA7 A077](https://l.runtimeterror.dev/pgp)
[^1]: Congrats? And also, *thank you.*

View file

@ -1,12 +1,12 @@
---
title: "Automating Security Camera Notifications With Home Assistant and Ntfy"
date: 2023-11-25
lastmod: 2023-11-27
lastmod: 2024-01-15
description: "Using the power of Home Assistant automations and Ntfy push notifications to level-up security camera motion detections."
featured: true
alias: automating-security-camera-notifications-with-home-assistant-and-ntfy
toc: true
comment: true
comments: true
thumbnail: thumbnail.png
series: Projects
tags:
@ -25,7 +25,7 @@ I figured I could combine the excellent [Reolink integration for Home Assistant]
### Alert on motion detection
{{% notice note "Ntfy Integration" %}}
Since manually configuring ntfy in Home Assistant via the [RESTful Notifications integration](easy-push-notifications-with-ntfy/#notify-configuration), I found that a [ntfy-specific integration](https://github.com/ivanmihov/homeassistant-ntfy.sh) was available through the [Home Assistant Community Store](https://hacs.xyz/) addon. That setup is a bit more flexible so I've switched my setup to use it instead:
Since manually configuring ntfy in Home Assistant via the [RESTful Notifications integration](/easy-push-notifications-with-ntfy#notify-configuration), I found that a [ntfy-specific integration](https://github.com/ivanmihov/homeassistant-ntfy.sh) was available through the [Home Assistant Community Store](https://hacs.xyz/) addon. That setup is a bit more flexible so I've switched my setup to use it instead:
```yaml
# configuration.yaml
notify:

View file

@ -21,7 +21,7 @@ tags:
- python
- api
- phpipam
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I [recently wrote](/tanzu-community-edition-k8s-homelab/#a-real-workload---phpipam) about getting started with VMware's [Tanzu Community Edition](https://tanzucommunityedition.io/) and deploying [phpIPAM](https://phpipam.net/) as my first real-world Kubernetes workload. Well I've spent much of my time since then working on a script which would help to populate my phpIPAM instance with a list of networks to monitor.

View file

@ -19,7 +19,7 @@ tags:
- linux
- shell
- regex
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
It's super handy when a Linux config file is loaded with comments to tell you precisely how to configure the thing, but all those comments can really get in the way when you're trying to review the current configuration.

View file

@ -20,7 +20,7 @@ tags:
- chromeos
- homelab
- iac
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I've lately been trying to do more with [Salt](https://saltproject.io/) at work, but I'm still very much a novice with that tool. I thought it would be great to have a nice little portable lab environment where I could deploy a few lightweight VMs and practice managing them with Salt - without impacting any systems that are actually being used for anything. Along the way, I figured I'd leverage [HashiCorp Vagrant](https://www.vagrantup.com/) to create and manage the VMs, which would provide a declarative way to define what the VMs should look like. The VM (or even groups of VMs) would be specified in a single file, and I'd bypass all the tedious steps of creating the virtual hardware, attaching the installation media, installing the OS, and performing the initial configuration. Vagrant will help me build up, destroy, and redeploy a development environment in a simple and repeatable way.

View file

@ -4,7 +4,7 @@ date: 2023-11-24
description: "I moved my homelab from VMware vSphere to Proxmox VE, and my only regret is that I didn't make this change sooner."
featured: false
toc: true
comment: true
comments: true
series: Tips # Projects, Code
tags:
- homelab

View file

@ -5,7 +5,7 @@ lastmod: 2023-12-22
description: "Deploying and configuring a self-hosted pub-sub notification handler, getting another server to send a notifcation when it boots, and integrating the notification handler into Home Assistant."
featured: false
toc: true
comment: true
comments: true
series: Projects
tags:
- android

View file

@ -21,7 +21,7 @@ tags:
- tanzu
- kubernetes
- shell
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Lately I've been spending some time [getting more familiar](/tanzu-community-edition-k8s-homelab/) with VMware's [Tanzu Community Edition](https://tanzucommunityedition.io/) Kubernetes distribution, but I'm still not quite familiar enough with the `tanzu` command line. If only there were a better way for me to discover the available commands for a given context and help me type them correctly...

View file

@ -23,7 +23,7 @@ tags:
- tailscale
- photon
- vpn
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
{{% notice note "ESXi-ARM Fling v1.10 Update" %}}
On July 20, 2022, VMware released a [major update](https://blogs.vmware.com/arm/2022/07/20/1-10/) for the ESXi-ARM Fling. Among [other fixes and improvements](https://flings.vmware.com/esxi-arm-edition#changelog), this version enables **in-place ESXi upgrades** and [adds support for the Quartz64's **on-board NIC**](https://twitter.com/jmcwhatever/status/1549935971822706688). To update, I:

View file

@ -57,7 +57,7 @@ Once that's done, I can try my redirect again - and, after a brief moment, it su
![Successful redirect](20210820_successful_redirect.png)
### Link custom domain
The whole point of this project is to *shorten* URLs, but I haven't done that yet. I'll want to link in my `go.bowdre.net` domain to use that in place of the rather unwieldy `https://sheets-url-shortener-vrw7x6wdzq-uc.a.run.app`. I do that by going back to the [Cloud Run console](https://console.cloud.google.com/run) and selecting the option at the top to **Manage Custom Domains**.
The whole point of this project is to *shorten* URLs, but I haven't done that yet. I'll want to link in my `go.bowdre.net` domain to use that in place of the rather unwieldy `https://sheets-url-shortener-somestring-uc.a.run.app`. I do that by going back to the [Cloud Run console](https://console.cloud.google.com/run) and selecting the option at the top to **Manage Custom Domains**.
![Manage custom domains](20210820_manage_custom_domain.png)
I can then use the **Add Mapping** button, select my `sheets-url-shortener` service, choose one of my verified domains (which I *think* are already verified since they're registered through Google Domains with the same account), and then specify the desired subdomain.
@ -73,8 +73,6 @@ It took a while for the domain mapping to go live once I've updated the record.
Once it did finally update, I was able to hit `https://go.bowdre.net` to get the error/landing page, complete with a valid SSL cert:
![Successful error!](20210820_landing_page.png)
And testing [go.bowdre.net/ghia](https://go.bowdre.net/ghia) works as well!
### Outro
I'm very pleased with how this quick little project turned out. Managing my shortened links with a Google Sheet is quite convenient, and I really like the complete lack of tracking or analytics. Plus I'm a sucker for an excuse to use a cloud technology I haven't played a lot with yet.
@ -82,9 +80,9 @@ And now I can hand out handy-dandy short links!
| Link | Description|
| --- | --- |
| [go.bowdre.net/coso](https://go.bowdre.net/coso) | Follow me on CounterSocial |
| [go.bowdre.net/conedoge](https://go.bowdre.net/conedoge) | 2014 Subaru BRZ autocross videos |
| [go.bowdre.net/cooltechshit](https://go.bowdre.net/cooltechshit) | A collection of cool tech shit (references and resources) |
| [go.bowdre.net/stuffiuse](https://go.bowdre.net/stuffiuse) | Things that I use (and think you should use too) |
| [go.bowdre.net/shorterer](https://go.bowdre.net/shorterer) | This post! |
| [go.bowdre.net/coso](https://l.runtimeterror.dev/coso) | Follow me on CounterSocial |
| [go.bowdre.net/conedoge](https://l.runtimeterror.dev/conedoge) | 2014 Subaru BRZ autocross videos |
| [go.bowdre.net/cooltechshit](https://l.runtimeterror.dev/cooltechshit) | A collection of cool tech shit (references and resources) |
| [go.bowdre.net/stuffiuse](https://l.runtimeterror.dev/stuffiuse) | Things that I use (and think you should use too) |
| [go.bowdre.net/shorterer](https://l.runtimeterror.dev/shorterer) | This post! |

View file

@ -23,7 +23,7 @@ tags:
- automation
- rest
- api
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I've been doing a bit of work lately to make my vRealize Automation setup more flexible and dynamic and less dependent upon hardcoded values. To that end, I thought it was probably about time to learn how to interact with the vRA REST API. I wrote this post to share what I've learned and give a quick crash course on how to start doing things with the API.

View file

@ -22,7 +22,7 @@ tags:
- cloud
- tailscale
- selfhosting
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I recently started using [Obsidian](https://obsidian.md/) for keeping notes, tracking projects, and just generally organizing all the information that would otherwise pass into my brain and then fall out the other side. Unlike other similar solutions which operate entirely in *The Cloud*, Obsidian works with Markdown files stored in a local folder[^sync], which I find to be very attractive. Not only will this allow me to easily transfer my notes between apps if I find something I like better than Obsidian, but it also opens the door to using `git` to easily back up all this important information.

View file

@ -19,7 +19,7 @@ codeLineNumbers: false # Override global value for showing of line numbers withi
tags:
- meta
- hugo
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
**Oops, I did it again.**

View file

@ -24,7 +24,7 @@ tags:
- containers
- iac
- packer
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I've been leveraging the open-source Tanzu Community Edition Kubernetes distribution for a little while now, both [in my home lab](/tanzu-community-edition-k8s-homelab) and at work, so I was disappointed to learn that VMware was [abandoning the project](https://github.com/vmware-tanzu/community-edition). TCE had been a pretty good fit for my needs, and now I needed to search for a replacement. VMware is offering a free version of Tanzu Kubernetes Grid as a replacement, but it comes with a license solely for non-commercial use so I wouldn't be able to use it at work. And I'd really like to use the same solution in both environments to make development and testing easier on me.

View file

@ -23,7 +23,7 @@ tags:
- certs
- cluster
- containers
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Not long ago, I [deployed a Tanzu Community Edition Kubernetes cluster in my homelab](/tanzu-community-edition-k8s-homelab/), and then I fumbled through figuring out how to [log into it from a different device](/logging-in-tce-cluster-from-new-device/) than the one I'd used for deploying the cluster from the `tanzu` cli. That setup works great for playing with Kubernetes in my homelab but I'd love to do some Kubernetes with my team at work and I really need the ability to authenticate multiple users with domain credentials for that.

View file

@ -19,7 +19,7 @@ tags:
- vmware
- kubernetes
- tanzu
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
When I [set up my Tanzu Community Edition environment](/tanzu-community-edition-k8s-homelab/), I did so from a Linux VM since the containerized Linux environment on my Chromebook doesn't support the `kind` bootstrap cluster used for the deployment. But now that the Kubernetes cluster is up and running, I'd like to be able to connect to it directly without the aid of a jumpbox. How do I get the appropriate cluster configuration over to my Chromebook?

View file

@ -21,7 +21,7 @@ tags:
- tanzu
- containers
- security
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Now that VMware [has released](https://blogs.vmware.com/vsphere/2022/01/announcing-availability-of-vsphere-7-update-3c.html) [vCenter 7.0U3c](https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-vcenter-server-70u3c-release-notes.html) to resolve the Log4Shell vulnerabilities I thought it might be fun to run a security scan against the upgraded VCSA in my homelab to see how it looks. Of course, I don't actually have a security scanner in that environment so I'll need to deploy one.

View file

@ -19,7 +19,7 @@ tags:
- vmware
- powercli
- powershell
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I recently needed to export a list of all the Linux VMs in a rather large vSphere environment spanning multiple vCenters (and the entire globe), and I wanted to include information about which virtual datacenter each VM lived in to make it easier to map VMs to their physical location.

View file

@ -18,7 +18,7 @@ series: Code
tags:
- powershell
- windows
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
We've been working lately to use [HashiCorp Packer](https://www.packer.io/) to standardize and automate our VM template builds, and we found a need to pull in all of the contents of a specific directory on an internal web server. This would be pretty simple for Linux systems using `wget -r`, but we needed to find another solution for our Windows builds.

View file

@ -20,7 +20,7 @@ tags:
- powershell
- windows
- powercli
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
{{% notice note "Fix available" %}}
VMware has released a fix for this problem in the form of [ESXi 7.0 Update 3k](https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-esxi-70u3k-release-notes.html#resolvedissues):

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -0,0 +1,182 @@
---
title: "Publish Services with Cloudflare Tunnel"
date: 2024-01-15
# lastmod: 2024-01-13
description: "Exploring Cloudflare Tunnel as an alternative to Tailscale Funnel for secure public access to internal resources."
featured: false
toc: true
comments: true
series: Tips # Projects, Code
tags:
- cloud
- containers
- docker
- networking
- selfhosting
---
I've written a bit lately about how handy [Tailscale Serve and Funnel](/tailscale-ssh-serve-funnel/) can be, and I continue to get a lot of great use out of those features. But not *every* networking nail is best handled with a Tailscale-shaped hammer. Funnel has two limitations that might make it less than ideal for certain situations.
First, sites served with Funnel can only have a hostname in the form of `server.tailnet-name.ts.net`. You can't use a custom domain for this, but you might not always want to advertise that a service is shared via Tailscale. Second, Funnel connections have an undisclosed bandwidth limit, which could cause problems if you're hoping to serve media through the Funnel.
For instance, I've started using [Immich](https://immich.app/) as a self-hosted alternative to Google Photos. Using Tailscale Serve to make my Immich server available on my Tailnet works beautifully, and I initially set up a Funnel connection to use for when I wanted to share certain photos, videos, and albums externally. I quickly realized that it took *f o r e v e r* to load the page when those links were shared publicly. I probably won't share a lot of those public links but I'd like them to be a bit more responsive when I do.
I went looking for another solution, and I found one in a suite of products I already use.
### Overview
I've been using [Cloudflare's generious free plan](https://www.cloudflare.com/plans/free/) for DNS, content caching, page/domain redirects, email forwarding, and DDoS mitigation[^more] across my dozen or so domains. In addition to these "basic" services and features, Cloudflare also offers a selection of [Zero Trust Network Access](https://www.cloudflare.com/products/zero-trust/zero-trust-network-access/) products, and one of those is [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/) - also available with a generous free plan.
[^more]: And a ton of other things I'm forgetting right now.
In some ways, Cloudflare Tunnel is quite similar to Tailscale Funnel. Both provide a secure way to publish a resource on the internet without requiring a public IP address, port forwarding, or firewall configuration. Both use a lightweight agent on your server to establish an encrypted outbound tunnel, and inbound traffic gets routed into that tunnel through the provider's network. And both solutions automatically provision trusted SSL certificates to keep traffic safe and browsers happy.
Tailscale Funnel is very easy to set up, and it doesn't require any additional infrastructure, not even a domain name. There aren't a lot of controls available with Funnel - it's basically on or off, and bound to one of three port numbers. You don't get to pick the domain name where it's served, just the hostname of the Tailscale node - and if you want to share multiple resources on the same host you'll [need to get creative](/tailscale-serve-docker-compose-sidecar/). I think this approach is really ideal for quick development and testing scenarios.
For longer-term, more production-like use, Cloudflare Tunnels is a pretty great fit. It ties in well with existing Cloudflare services, doesn't enforce a reduced bandwidth limit, and provides a bit more flexibility for how your resource will be presented to the web. It can also integrate tightly with the rest of Cloudflare's Zero Trust offerings to easily provide access controls to further protect your resource. It does, however, require a custom domain managed with Cloudflare DNS in order to work[^dns].
[^dns]: Cloudflare Tunnel lets you choose what hostname and domain name should be used for fronting your tunnel, and it even takes care of configuring the required DNS record automagically.
For my specific Immich use case, I decided to share my instance via Tailscale Serve for internal access and Cloudflare Tunnel for public shares, and I used a similar sidecar approach to make it work without too much fuss. For the purposes of this blog post, though, I'm going to run through a less complicated example[^complexity].
[^complexity]: My Immich stack is using ~10 containers and I don't really feel like documenting that all here - not yet, at least.
### Speedtest Demo
I'm going to deploy a quick [SpeedTest by OpenSpeedTest](https://github.com/openspeedtest/Speed-Test) container, and proxy it with both Tailscale Funnel and Cloudflare Tunnel so that I can compare the bandwidth of the two tunnel solutions directly.
I'll start with a *very* basic Docker Compose definition for just the Speedtest container:
```yaml
# torchlight! {"lineNumbers":true}
# docker-compose.yml
services:
speedtest:
image: openspeedtest/latest
container_name: speedtest
restart: unless-stopped
```
#### Tailscale Funnel
And, as in [my last post](/tailscale-serve-docker-compose-sidecar/) I'll add in my Tailscale sidecar to enable funnel:
```yaml
# torchlight! {"lineNumbers":true}
# docker-compose.yml
services:
speedtest:
image: openspeedtest/latest
container_name: speedtest
restart: unless-stopped
network_mode: service:tailscale # [tl! ++:start focus:start]
tailscale:
image: ghcr.io/jbowdre/tailscale-docker:latest
container_name: speedtest-tailscaled
restart: unless-stopped
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?err}
TS_HOSTNAME: ${TS_HOSTNAME:-tailscale-sidecar}
TS_STATE_DIR: "/var/lib/tailscale/"
TS_EXTRA_ARGS: ${TS_EXTRA_ARGS:-}
TS_SERVE_PORT: ${TS_SERVE_PORT:-}
TS_FUNNEL: ${TS_FUNNEL:-} # [tl! ++:end focus:end]
```
{{% notice note "Network Mode" %}}
I set `network_mode: service:tailscale` on the `speedtest` container so that it will share its network interface with the `tailscale` container. This allows Tailscale Serve/Funnel to proxy `speedtest` at `http://localhost:3000`, which is nice since Tailscale doesn't currently/officially support proxying remote hosts.
{{% /notice %}}
I'll set up a new auth key in the [Tailscale Admin Portal](https://login.tailscale.com/admin/settings/keys), and insert that (along with hostname, port, and funnel configs) into my `.env` file:
```shell
# torchlight! {"lineNumbers":true}
# .env
TS_AUTHKEY=tskey-auth-somestring-somelongerstring
TS_HOSTNAME=speedtest
TS_EXTRA_ARGS=--ssh
TS_SERVE_PORT=3000 # the port the speedtest runs on by default
TS_FUNNEL=true
```
A quick `docker compose up -d` and my new speedtest is alive!
First I'll hit it at `http://speedtest.tailnet-name.ts.net:3000` to access it purely inside of my Tailnet:
![Speedtest from within the tailnet](speedtest-tailnet.png)
Not bad! Now let's see what happens when I disable Tailscale on my laptop and hit the public Funnel endpoint at `https://speedtest.tailnet-name.ts.net`:
![Speedtest from funnel](speedtest-funnel.png)
Oof. Routing traffic through the Funnel dropped the download by ~25% and the upload by **~90%**, not to mention the significant ping increase.
#### Cloudflare Tunnel
Alright, let's throw a Cloudflare Tunnel on there and see what happens.
To start that process, I'll log into my [Cloudflare dashboard](https://dash.cloudflare.com) and then use the side navigation to make my way to the **Zero Trust** (AKA "Cloudflare One") area. From there, I'll drill down through **Access -> Tunnels** and click on **+ Create a tunnel**. I'll give it an appropriate name like `speedtest` and then click **Save tunnel**.
Now Cloudflare helpfully provides installation instructions for a variety of different platforms. I'm doing that Docker thing so I'll click the appropriate button and review that command snippet:
![Tunnel installation instructions](install-connector.png)
I can easily adapt that and add it to my Docker Compose setup[^network-mode]:
```yaml
# torchlight! {"lineNumbers":true}
# docker-compose.yml
services:
speedtest:
image: openspeedtest/latest
container_name: speedtest
restart: unless-stopped
network_mode: service:tailscale
tailscale: # [tl! collapse:start]
image: ghcr.io/jbowdre/tailscale-docker:latest
container_name: speedtest-tailscaled
restart: unless-stopped
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?err}
TS_HOSTNAME: ${TS_HOSTNAME:-tailscale}
TS_STATE_DIR: "/var/lib/tailscale/"
TS_EXTRA_ARGS: ${TS_EXTRA_ARGS:-}
TS_SERVE_PORT: ${TS_SERVE_PORT:-}
TS_FUNNEL: ${TS_FUNNEL:-} # [tl! collapse:end]
cloudflared: # [tl! ++:start focus:start]
image: cloudflare/cloudflared
container_name: speedtest-cloudflared
restart: unless-stopped
command:
- tunnel
- --no-autoupdate
- run
- --token
- ${CLOUDFLARED_TOKEN}
network_mode: service:tailscale # [tl! ++:end focus:end]
```
[^network-mode]: Setting the `network_mode` isn't strictly necessary for the `cloudflared` container since Cloudflare Tunnel *does* support proxying remote hosts, but I'll just stick with it here for consistency.
After dropping the value for `CLOUDFLARED_TOKEN` into my `.env` file, I can do another `docker compose up -d` to bring this online - and that status will be reflected back on the config page as well:
![Connector is alive!](connector-online.png)
I'll click **Next** and proceed with the rest of the configuration, which consists of picking a public hostname for the frontend and defining the private service for the backend:
![Tunnel configuration](tunnel-configuration.png)
I can click **Save tunnel** and... that's it. My tunnel is live, and I can now reach my speedtest at `https://speedtest.runtimeterror.dev`. Let's see how it does:
![Cloudflare Tunnel speedtest](speedtest-cloudflared.png)
So that's *much* faster than Tailscale Funnel, and even faster than a direct transfer within the Tailnet. Cloudflare Tunnel should work quite nicely for sharing photos publicly from my Immich instance.
#### Bonus: Access Control
But what if I don't want *just anyone* to be able to use my new speedtest (or access my Immich instance)? Defining an application in Cloudflare One will let me set some limits.
So I'll go to **Access -> Applications** and select that I'm adding a **Self-hosted** application. I can then do the basic configuration, basically just telling Cloudflare that I'd like to protect the `https://speedtest.runtimeterror.dev` app:
![Defining the application](define-application.png)
I can leave the rest of that page with the default selections so I'll scroll down and click **Next**.
Now I need to create a policy to apply to this application. I'm going to be simple and just say that anyone with an `@runtimeterror.dev` email address should be able to use my speedtest:
![Creating a policy](create-policy.png)
Without any external identity providers connected, Cloudflare will default to requiring authentication via a one-time PIN sent to an input email address. That's pretty easy, and it pairs well with allowing access based on email address attributes. There are a bunch of other options I could configure if I wanted... but my needs are simple so I'll just click through and save this new application config.
Now, if I try to visit my speedtest with a new session I'll get automatically routed to the Cloudflare Access challenge which will prompt for my email address.
![Access challenge](access-challenge.png)
If my email is on the approved list (that is, if it ends with `@runtimeterror.dev`), I'll get emailed a code which I can then use to log in and access the speedtest. If not, I won't get in. And since this thing is served through a Cloudflare Tunnel (rather than a public IP address merely advertised via DNS) there isn't any way to bypass Cloudflare's authentication challenge.
### Conclusion
This has been a quick demo of how easy it is to configure a Cloudflare Tunnel to securely publish resources on the web. I really like being able to share a service publicly without having to manage DNS, port-forwarding, or firewall configurations, and the ability to offload authentication and authorization to Cloudflare is a big plus. I still don't think Tailscale can be beat for sharing stuff internally, but I think Cloudflare Tunnels make more sense for long-term public sharing. And it's awesome that I can run both solutions side-by-side to really get the best of both when I need it.

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View file

@ -19,7 +19,7 @@ tags:
- vmware
- vsphere
- homelab
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Way back in 2020, VMware released vSphere 7 Update 1 and introduced the new [vSphere Clustering Services (vCLS)](https://core.vmware.com/resource/introduction-vsphere-clustering-service-vcls) to improve how cluster services like the Distributed Resource Scheduler (DRS) operate. vCLS deploys lightweight agent VMs directly on the cluster being managed, and those VMs provide a decoupled and distributed control plane to offload some of the management responsibilities from the vCenter server.

View file

@ -5,7 +5,7 @@ lastmod: 2023-12-22
description: "A hasty Salt state to deploy netdata monitoring and publish it internally on my tailnet with Tailscale Serve"
featured: false
toc: true
comment: true
comments: true
series: Code
tags:
- homelab

View file

@ -20,7 +20,7 @@ tags:
- hugo
- meta
- shell
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
In case you missed [the news](/hello-hugo), I recently migrated this blog from a site built with Jekyll to one built with Hugo. One of Hugo's cool features is the concept of [Page Bundles](https://gohugo.io/content-management/page-bundles/), which _bundle_ a page's resources together in one place instead of scattering them all over the place.

View file

@ -24,7 +24,7 @@ tags:
- networking
- security
- tailscale
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Not all that long ago, I shared about a [somewhat-complicated WireGuard VPN setup](/cloud-based-wireguard-vpn-remote-homelab-access/) that I had started using to replace my previous OpenVPN solution. I raved about WireGuard's speed, security, and flexible (if complex) Cryptokey Routing, but adding and managing peers with WireGuard is a fairly manual (and tedious) process. And while I thought I was pretty clever for using a WireGuard peer in GCP to maintain a secure tunnel into my home network without having to punch holes through my firewall, routing all my traffic through The Cloud wasn't really optimal[^egress_fees].

View file

@ -23,7 +23,7 @@ tags:
- chat
- selfhosting
- caddy
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
**Non-technical users deserve private communications, too.**

View file

@ -5,7 +5,7 @@ lastmod: 2023-11-13
description: "Syntax highlighting powered by the Torchlight.dev API makes it easier to dress up code blocks. Here's an overview of what I did to replace this blog's built-in Hugo highlighter (Chroma) with Torchlight."
featured: false
toc: true
comment: true
comments: true
series: Projects # Projects, Code
tags:
- javascript

View file

@ -5,7 +5,7 @@ date: 2023-10-15
description: "Quick notes on using `systemctl edit` to override a systemd service to delay its startup."
featured: false
toc: false
comment: true
comments: true
series: Tips # Projects, Code
tags:
- crostini

View file

@ -22,7 +22,7 @@ tags:
- wireguard
- containers
- selfhosting
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
I've shared in the past about how I use [custom search engines in Chrome](/abusing-chromes-custom-search-engines-for-fun-and-profit/) as quick web shortcuts. And I may have mentioned [my love for Tailscale](/tags/tailscale/) a time or two as well. Well I recently learned of a way to combine these two passions: [Tailscale golink](https://github.com/tailscale/golink). The [golink announcement post on the Tailscale blog](https://tailscale.com/blog/golink/) offers a great overview of the service:
> Using golink, you can create and share simple go/name links for commonly accessed websites, so that anyone in your network can access them no matter the device theyre on — without requiring browser extensions or fiddling with DNS settings. And because golink integrates with Tailscale, links are private to users in your tailnet without any separate user management, logins, or security policies.

View file

@ -22,7 +22,7 @@ tags:
- networking
- security
- tailscale
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
You might remember that I'm a [pretty big fan](/secure-networking-made-simple-with-tailscale/) of [Tailscale](https://tailscale.com), which makes it easy to connect your various devices together in a secure [tailnet](https://tailscale.com/kb/1136/tailnet/), or private network. Tailscale is super simple to set up on most platforms, but you'll need to [install it manually](https://tailscale.com/download/linux/static) if there isn't a prebuilt package for your system.

View file

@ -1,11 +1,11 @@
---
title: "Tailscale Serve in a Docker Compose Sidecar"
date: 2023-12-30
# lastmod: 2023-12-28
lastmod: 2024-01-01
description: "Using Docker Compose to deploy containerized applications and make them available via Tailscale Serve and Tailscale Funnel"
featured: false
toc: true
comment: true
comments: true
series: Projects
tags:
- containers
@ -120,6 +120,7 @@ There's also a [sample `docker-compose.yml`](https://github.com/jbowdre/tailscal
services:
tailscale:
image: ghcr.io/jbowdre/tailscale-docker:latest
restart: unless-stopped
container_name: tailscale
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?err} # from https://login.tailscale.com/admin/settings/authkeys
@ -133,6 +134,7 @@ services:
- ./ts_data:/var/lib/tailscale/ # the mount point should match TS_STATE_DIR
myservice:
image: nginxdemos/hello
restart: unless-stopped
network_mode: "service:tailscale" # use the tailscale network service's network
```
@ -211,6 +213,7 @@ And I can add the corresponding `docker-compose.yml` to go with it:
services:
tailscale: # [tl! focus:start]
image: ghcr.io/jbowdre/tailscale-docker:latest
restart: unless-stopped
container_name: cyberchef-tailscale
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?err}
@ -286,6 +289,7 @@ I adapted the [example `docker-compose.yml`](https://miniflux.app/docs/dacker.ht
services:
tailscale: # [tl! focus:start]
image: ghcr.io/jbowdre/tailscale-docker:latest
restart: unless-stopped
container_name: miniflux-tailscale
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?err}
@ -299,6 +303,7 @@ services:
- ./ts_data:/var/lib/tailscale/ # [tl! focus:end]
miniflux:
image: miniflux/miniflux:latest
restart: unless-stopped
container_name: miniflux
depends_on:
db:
@ -312,6 +317,7 @@ services:
network_mode: "service:tailscale" # [tl! focus]
db:
image: postgres:15
restart: unless-stopped
container_name: miniflux-db
environment:
- POSTGRES_USER=${DB_USER}

View file

@ -5,7 +5,7 @@ date: 2023-12-20
description: "Exploring some of my favorite Tailscale addon features: SSH, Serve, and Funnel."
featured: false
toc: true
comment: true
comments: true
series: Tips # Projects, Code
tags:
- homelab

View file

@ -23,7 +23,7 @@ tags:
- containers
- tanzu
- homelab
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
Back in October, VMware [announced](https://tanzu.vmware.com/content/blog/vmware-tanzu-community-edition-announcement) [Tanzu Community Edition](https://tanzucommunityedition.io/) as way to provide "a full-featured, easy-to-manage Kubernetes platform thats perfect for users and learners alike." TCE bundles a bunch of open-source components together in a modular, "batteries included but swappable" way:

View file

@ -19,7 +19,7 @@ tags:
- vmware
- homelab
- vsphere
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
You may have heard that there's a new vSphere release out in the wild - [vSphere 8, which just reached Initial Availability this week](https://advocacy.vmware.com/Article/Redirect/9cfbc1b1-207f-4885-a520-cc0bfafcd6c0?uc=197618&g=2d17264e-593a-492d-8d91-3a2155e835f1&f=3104867). Upgrading the vCenter in my single-host homelab is a very straightforward task, and using the included Lifecycle Manager would make quick work of patching a cluster of hosts... but things get a little trickier with a single host. I could write the installer ISO to a USB drive, boot the host off of that, and go through the install interactively, but what if physical access to the host is kind of inconvenient?

View file

@ -19,7 +19,7 @@ tags:
- vmware
- vsphere
- python
comment: true # Disable comment if false.
comments: true # Disable comment if false.
---
VMware vCenter does wonders for abstracting away the layers of complexity involved in managing a large virtual infrastructure, but when something goes wrong it can be challenging to find exactly where the problem lies. And it can be even harder to proactively address potential issues before they occur.

View file

@ -6,7 +6,7 @@ timeless: true
draft: false
description: "This blog has migrated from virtuallypotato.com to runtimeterror.dev."
toc: false
comment: true
comments: true
tags:
- meta
---

View file

@ -8,7 +8,7 @@ showReadTime = false
timeless = true
title = "SimpleX Chat"
+++
*You can **[contact me on SimpleX Chat](https://simplex.chat/contact/#/?v=1-2&smp=smp%3A%2F%2FkYx5LmVD9FMM8hJN4BQqL4WmeUNZn8ipXsX2UkBoiHE%3D%40smp.vpota.to%2FFLy56WLZ79Xda3gW0BjUWDotP6uaparF%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAZTkRAbrxefYZbb5Qypb9BXfuN0X0tzSPEv682DkNcn0%253D)** by clicking that link or scanning the QR code below.*
*You can **[contact me on SimpleX Chat](https://l.runtimeterror.dev/simplex-chat-invite)** by clicking that link or scanning the QR code below.*
![](/images/simplex-invite.png)
@ -32,7 +32,7 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server
| | |
| --- | --- |
| [![](https://status.vpota.to/api/badge/6/uptime)](https://status.vpota.to/status/simplex) | [[details](https://jbowdre.url.lol/smp_status)] |
| [![](https://status.vpota.to/api/badge/6/uptime)](https://status.vpota.to/status/simplex) | [[details](https://l.runtimeterror.dev/smp_status)] |
---
@ -42,7 +42,7 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server
| | |
| --- | --- |
| [![](https://status.vpota.to/api/badge/4/uptime)](https://status.vpota.to/status/simplex) | [[details](https://jbowdre.url.lol/smp1_status)] |
| [![](https://status.vpota.to/api/badge/4/uptime)](https://status.vpota.to/status/simplex) | [[details](https://l.runtimeterror.dev/smp1_status)] |
---
@ -52,4 +52,4 @@ Just add these in the SimpleX app at **Settings > Network & servers > SMP server
| | |
| --- | --- |
| [![](https://status.vpota.to/api/badge/5/uptime)](https://status.vpota.to/status/simplex) | [[details](https://jbowdre.url.lol/smp2_status)] |
| [![](https://status.vpota.to/api/badge/5/uptime)](https://status.vpota.to/status/simplex) | [[details](https://l.runtimeterror.dev/smp2_status)] |

View file

@ -10,9 +10,10 @@
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<!-- {{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }} -->
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
@ -40,7 +41,10 @@
{{ range (.GetTerms "tags") }}
<category>{{ .LinkTitle }}</category>{{ end }}
<guid>{{ .Permalink }}</guid>
<description>{{ .Summary | plainify }}</description>
{{- $content := replaceRE "a href=\"(#.*?)\"" (printf "%s%s%s" "a href=\"" .Permalink "$1\"") .Content -}}
{{- $content = replaceRE "img src=\"(.*?)\"" (printf "%s%s%s" "img src=\"" .Permalink "$1\"") $content -}}
{{- $content = replaceRE "<svg.*</svg>" "" $content -}}
<description>{{ $content | html }}</description>
</item>
{{ end }}
</channel>

View file

@ -1,12 +1,21 @@
{{ if .Site.Params.utterancesRepo }}
{{ if isset site.Params "giscusrepo" }}
<br>
<div class="post_comments">
<script src="https://utteranc.es/client.js"
repo="{{.Site.Params.utterancesRepo}}"
issue-term="{{.Site.Params.utterancesIssueTerm | default "pathname"}}"
theme="{{.Site.Params.utterancesTheme | default "github-dark-orange"}}"
crossorigin="anonymous"
async>
<script src="https://giscus.runtimeterror.dev/client.js"
data-repo="{{ .Site.Params.giscusRepo }}"
data-repo-id="{{ .Site.Params.giscusRepoId }}"
data-category="{{ .Site.Params.giscusCategory }}"
data-category-id="{{ .Site.Params.giscusCategoryId }}"
data-mapping="{{ .Site.Params.giscusMapping }}"
data-strict="{{ .Site.Params.giscusStrict }}"
data-reactions-enabled="{{ .Site.Params.giscusReactions }}"
data-emit-metadata="{{ .Site.Params.giscusEmitMetadata }}"
data-input-position="{{ .Site.Params.giscusInputPosition }}"
data-theme="{{ .Site.Params.giscusTheme }}"
data-lang="{{ .Site.Params.giscusLang }}"
data-loading="{{ .Site.Params.giscusLoading }}"
crossorigin="{{ .Site.Params.giscusCrossOrigin }}"
async>
</script>
</div>
{{ end }}

View file

@ -6,14 +6,14 @@
[context.production]
command = """
hugo --minify
hugo
npm i @torchlight-api/torchlight-cli
npx torchlight
"""
[context.preview]
command = """
hugo --minify --environment preview
hugo --environment preview
npm i @torchlight-api/torchlight-cli
npx torchlight
"""
@ -24,7 +24,7 @@
[context.drafts]
command = """
hugo --minify --environment drafts -D
hugo --environment drafts -D
npm i @torchlight-api/torchlight-cli
npx torchlight
"""

View file

@ -197,7 +197,10 @@ ul.pagination li {
}
.statuslol_time a:hover,
.statuslol_time a:active,
.statuslol_time a.active {
.statuslol_time a:active {
color: var(--hover) !important;
}
small[style^="opacity: .5"] {
opacity: 1 !important;
}

View file

@ -6,7 +6,7 @@
--base01: #282828; /* off-bg */
--base02: #383838; /* inner-bg */
--base03: #585858; /* muted */
--base04: #959494; /* off-fg */
--base04: #cfcfcf; /* off-fg */
--base05: #d8d8d8; /* fg */
--base06: #e8e8e8;
--base07: #5f8700; /* user prompt */