Merge branch 'main' into drafts

This commit is contained in:
John Bowdre 2024-01-21 08:06:12 -06:00
commit 791659336b
117 changed files with 4304 additions and 259 deletions

View file

@ -1,14 +0,0 @@
name: Daily build
on:
schedule:
- cron: "0 13 * * *"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Trigger build webhook on Netlify
run: curl -s -X POST "https://api.netlify.com/build_hooks/${TOKEN}"
env:
TOKEN: ${{ secrets.NETLIFY_CRON_BUILD_HOOK }}

View file

@ -0,0 +1,54 @@
name: Deploy to neocities
# only run on changes to main
on:
push:
branches:
- main
concurrency: # prevent concurrent deploys doing strange things
group: deploy-to-neocities
cancel-in-progress: true
# Default to bash
defaults:
run:
shell: bash
jobs:
deploy:
runs-on: ubuntu-latest
env:
HUGO_VERSION: "0.121.1"
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--minify \
--baseURL "https://runtimeterror.dev/"
- name: Highlight with Torchlight
run: |
npm i @torchlight-api/torchlight-cli
npx torchlight
- name: Deploy to neocities
uses: bcomnes/deploy-to-neocities@v1
with:
api_token: ${{ secrets.NEOCITIES_API_TOKEN }}
cleanup: false
dist_dir: public

View file

@ -7,52 +7,32 @@ description: "This is a new post about..."
featured: false
toc: true
comments: true
series: Tips # Projects, Code
categories: Tips # Backstage, ChromeOS, Code, Self-Hosting, VMware
tags:
- 3dprinting
- activedirectory
- android
- api
- automation
- availability
- caddy
- certs
- chat
- chrome
- chromeos
- cloud
- cluster
- containers
- crostini
- docker
- gcp
- homeassistant
- homelab
- hugo
- iac
- javascript
- kubernetes
- linux
- logs
- meta
- networking
- openssl
- packer
- powercli
- powershell
- python
- regex
- rest
- salt
- security
- selfhosting
- serverless
- shell
- tailscale
- tasker
- terraform
- vmware
- vpn
- windows
- wireguard
- wsl

1
assets/js/back-to-top.js Normal file
View file

@ -0,0 +1 @@
"use strict";function addBackToTop(){var o,t,e,n,i=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},r=i.backgroundColor,d=void 0===r?"#000":r,a=i.cornerOffset,c=void 0===a?20:a,s=i.diameter,l=void 0===s?56:s,u=i.ease,p=void 0===u?function(o){return.5*(1-Math.cos(Math.PI*o))}:u,m=i.id,h=void 0===m?"back-to-top":m,b=i.innerHTML,v=void 0===b?'<svg viewBox="0 0 24 24"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></svg>':b,f=i.onClickScrollTo,x=void 0===f?0:f,w=i.scrollContainer,g=void 0===w?document.body:w,k=i.scrollDuration,y=void 0===k?100:k,T=i.showWhenScrollTopIs,M=void 0===T?1:T,z=i.size,E=void 0===z?l:z,C=i.textColor,L=void 0===C?"#fff":C,N=i.zIndex,I=void 0===N?1:N,A=g===document.body,B=A&&document.documentElement;o=Math.round(.43*E),t=Math.round(.29*E),e="#"+h+"{background:"+d+";-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;bottom:"+c+"px;-webkit-box-shadow:0 2px 5px 0 rgba(0,0,0,.26);-moz-box-shadow:0 2px 5px 0 rgba(0,0,0,.26);box-shadow:0 2px 5px 0 rgba(0,0,0,.26);color:"+L+";cursor:pointer;display:block;height:"+E+"px;opacity:1;outline:0;position:fixed;right:"+c+"px;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-transition:bottom .2s,opacity .2s;-o-transition:bottom .2s,opacity .2s;-moz-transition:bottom .2s,opacity .2s;transition:bottom .2s,opacity .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:"+E+"px;z-index:"+I+"}#"+h+" svg{display:block;fill:currentColor;height:"+o+"px;margin:"+t+"px auto 0;width:"+o+"px}#"+h+".hidden{bottom:-"+E+"px;opacity:0}",(n=document.createElement("style")).appendChild(document.createTextNode(e)),document.head.insertAdjacentElement("afterbegin",n);var D=function(){var o=document.createElement("div");return o.id=h,o.className="hidden",o.innerHTML=v,o.addEventListener("click",function(o){o.preventDefault(),function(){var o="function"==typeof x?x():x,t=window,e=t.performance,n=t.requestAnimationFrame;if(y<=0||void 0===e||void 0===n)return q(o);var i=e.now(),r=j(),d=r-o;n(function o(t){var e=Math.min((t-i)/y,1);q(r-Math.round(p(e)*d)),e<1&&n(o)})}()}),document.body.appendChild(o),o}(),H=!0;function S(){j()>=M?function(){if(!H)return;D.className="",H=!1}():function(){if(H)return;D.className="hidden",H=!0}()}function j(){return g.scrollTop||B&&document.documentElement.scrollTop||0}function q(o){g.scrollTop=o,B&&(document.documentElement.scrollTop=o)}(A?window:g).addEventListener("scroll",S),S()}

3475
assets/js/lunr.js Normal file

File diff suppressed because it is too large Load diff

45
assets/js/search.js Normal file
View file

@ -0,0 +1,45 @@
// based on https://victoria.dev/blog/add-search-to-hugo-static-sites-with-lunr/
function displayResults (results, store) {
const searchResults = document.getElementById('results');
if (results.length) {
let resultList = '';
for (const n in results) {
const item = store[results[n].ref];
resultList += '<li><a href="' + item.url + '">' + item.title + '</a></li>'
if (item.description)
resultList += '<p>' + item.description + '</p>'
else
resultList += '<p>' + item.content.substring(0, 150) + '...</p>'
}
searchResults.innerHTML = resultList;
} else {
searchResults.innerHTML = 'No results found.';
}
}
const params = new URLSearchParams(window.location.search);
const query = params.get('query');
if (query) {
document.getElementById('search-query').setAttribute('value', query);
const idx = lunr(function () {
this.ref('id')
this.field('title', {
boost: 15
})
this.field('tags')
this.field('content', {
boost: 10
})
for (const key in window.store) {
this.add({
id: key,
title: window.store[key].title,
tags: window.store[key].tags,
content: window.store[key].content
})
}
})
const results = idx.search(query);
displayResults(results, window.store)
}

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

@ -11,7 +11,7 @@ enableInlineShortcodes = true
# sectionPagesMenu = "main"
[outputs]
home = ['html', 'rss', 'json']
home = ['html', 'rss']
section = ['html']
taxonomy = ['html',]
term = ['html', 'rss']
@ -58,5 +58,8 @@ enableInlineShortcodes = true
[taxonomies]
tag = "tags"
series = "series"
category = "categories"
[minify]
disableXML = true
minifyOutput = true

View file

@ -5,21 +5,21 @@
# weight = 10
[[main]]
identifier = "projects"
name = "projects"
url = "/series/projects/"
identifier = "self-hosting"
name = "self-hosting"
url = "/categories/self-hosting/"
weight = 1
[[main]]
identifier = "tips"
name = "tips"
url = "/series/tips/"
url = "/categories/tips/"
weight = 1
[[main]]
identifier = "code"
name = "code"
url = "/series/code/"
url = "/categories/code/"
weight = 1
[[main]]

View file

@ -18,7 +18,7 @@ giscusInputPosition = "bottom"
giscusLang = "en"
giscusLoading = "lazy"
giscusMapping = "og:title"
giscusReactions = "1"
giscusReactions = "0"
giscusRepo = "jbowdre/site-comments"
giscusRepoId = "R_kgDOKKEGDw"
giscusStrict = "0"
@ -171,8 +171,8 @@ title = "hugo"
url = "https://gohugo.io"
[[powerLinks]]
title = "netlify"
url = "https://www.netlify.com"
title = "neocities"
url = "https://neocities.org/about"
[[powerLinks]]
title = "risotto"
@ -183,8 +183,8 @@ title = "torchlight"
url = "https://torchlight.dev"
[[powerLinks]]
title = "cabin"
url = "https://withcabin.com/privacy/runtimeterror.dev"
title = "tinylytics"
url = "https://tinylytics.app/home"
[[verifyLinks]]
title = "omg.lol"

View file

@ -1,12 +1,13 @@
+++
title = "404'd!"
aliases = ["not_found"]
noindex = true
timeless = true
comments = true
+++
We're not sure what you were looking for but it's not here.
![](/images/nothing-to-see-here.gif)
![Animated GIF from the movie "The Naked Gun". A man in the foreground proclaims "Please disperse. Nothing to see here." while a building explodes in the background.](/images/nothing-to-see-here.gif)
Maybe head back [home](/)?

View file

@ -11,11 +11,11 @@ You've (somehow) managed to stumble upon my dark corner of the internet[^1].
I've enjoyed tinkering with computers and their code since discovering I could alter variable values in [`GORILLA.BAS`](https://en.wikipedia.org/wiki/Gorillas_%28video_game%29) on my dad's work computer to imbue the thrown bananas with enough explosive power to level the entire city. I thought, "hey, that's neat," and then spent much of my childhood free time learning how *else* I could bend computers to my will.
Once I grew up[^2], I found a career in system administration, and I leveraged my passion for coding to write scripts to help manage systems more efficiently. While managing a global-scale VMware environment, I was tasked with implementing [vRealize Automation](/series/vra8) (now called "Aria Automation"). I didn't realize it at the time but that was the start of my DevOps transformation. I started thinking about infrastructure-as-code, and began using [HashiCorp Packer](https://github.com/jbowdre/packer-vsphere-templates) and a CI/CD pipeline to automatically build fully-up-to-date VM templates on a weekly cadence.
Once I grew up[^2], I found a career in system administration, and I leveraged my passion for coding to write scripts to help manage systems more efficiently. While managing a global-scale VMware environment, I was tasked with implementing [vRealize Automation](/categories/vmware) (now called "Aria Automation"). I didn't realize it at the time but that was the start of my DevOps transformation. I started thinking about infrastructure-as-code, and began using [HashiCorp Packer](https://github.com/jbowdre/packer-vsphere-templates) and a CI/CD pipeline to automatically build fully-up-to-date VM templates on a weekly cadence.
I'm now part of a small platform engineering team within that same large corporation, focused on leveraging DevOps thinking and tools to help our internal customers modernize how they operate IT, build code, and ship products, while designing solutions to help them accomplish those goals. It's a great blend of my virtual infrastructure operations background, hobbyist development experience, and hunger for solving problems, and I really enjoy applying these skills to solve interesting challenges at scale.
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 my off time, I tinker with new [projects](/categories/self-hosting) 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://l.runtimeterror.dev/my-autox-vids) or wrench on my 1974 Volkswagen Karmann Ghia.

View file

@ -0,0 +1,5 @@
---
title: Backstage
description: >
A peek behind the scenes at what it takes to run this site.
---

View file

@ -0,0 +1,5 @@
---
title: "ChromeOS"
description: >
My Chromebook is a lot more than just a browser.
---

View file

@ -0,0 +1,5 @@
---
title: Code
description: >
I did a programming and I wanted you to see.
---

View file

@ -0,0 +1,5 @@
---
title: Self-Hosting
description: >
Never met an app I didn't want to deploy.
---

View file

@ -0,0 +1,5 @@
---
title: Tips
description: >
I learned something the hard way so that you wouldn't have to.
---

View file

@ -0,0 +1,4 @@
---
title: "VMware"
description: "vSphere, vCenter, vRealize, vTanzu, vBroadcom..."
---

View file

@ -3,6 +3,7 @@ date: "2020-09-14T08:34:30Z"
thumbnail: qDTXt1jp3.png
featureImage: qDTXt1jp3.png
usePageBundles: true
categories: ChromeOS
tags:
- linux
- chromeos
@ -18,12 +19,12 @@ That's a pretty sweet setup, but I still needed a way to convert STL 3D models i
Enter "Crostini," Chrome OS's [Linux (Beta) feature](https://chromium.googlesource.com/chromiumos/docs/+/master/containers_and_vms.md). It consists of a hardened Linux VM named `termina` which runs (by default) a Debian Buster LXD container named `penguin` (though you can spin up just about any container for which you can find an [image](https://us.images.linuxcontainers.org/)) and some fancy plumbing to let Chrome OS and Linux interact in specific clearly-defined ways. It's a brilliant balance between offering the flexibility of Linux while preserving Chrome OS's industry-leading security posture.
![Neofetch in the Crostini terminal](lhTnVwCO3.png)
![Screenshot of the 'neofetch' utility](lhTnVwCO3.png)
There are plenty of great guides (like [this one](https://www.computerworld.com/article/3314739/linux-apps-on-chrome-os-an-easy-to-follow-guide.html)) on how to get started with Linux on Chrome OS so I won't rehash those steps here.
One additional step you will probably want to take is make sure that your Chromebook is configured to enable hyperthreading, as it may have [hyperthreading disabled by default](https://support.google.com/chromebook/answer/9340236). Just plug `chrome://flags/#scheduler-configuration` into Chrome's address bar, set it to `Enables Hyper-Threading on relevant CPUs`, and then click the button to restart your Chromebook. You'll thank me later.
![Enabling hyperthreading](LHax6lAwh.png)
![Screenshot of ChromeOS flags page showing that '#scheduler-configuration' is set to 'Enables Hyper-Threading on relevant CPUs](LHax6lAwh.png)
### The Software
I settled on using [FreeCAD](https://www.freecadweb.org/) for parametric modeling and [Ultimaker Cura](https://ultimaker.com/software/ultimaker-cura) for my GCODE slicer, but unfortunately getting them working cleanly wasn't entirely straightforward.
@ -68,7 +69,7 @@ Comment[de_DE]=Feature-basierter parametrischer Modellierer
MimeType=application/x-extension-fcstd
```
That's it! Get on with your 3D-modeling bad self.
![FreeCAD](qDTXt1jp3.png)
![Screenshot of FreeCAD showing a 3d model being worked on](qDTXt1jp3.png)
Now that you've got a model, be sure to [export it as an STL mesh](https://wiki.freecadweb.org/Export_to_STL_or_OBJ) so you can import it into your slicer.
#### Ultimaker Cura
@ -88,12 +89,12 @@ sudo apt update && sudo apt install menulibre # [tl! .cmd:2]
menulibre
```
Just plug in the relevant details (you can grab the appropriate icon [here](https://github.com/Ultimaker/Cura/blob/master/icons/cura-128.png)), hit the filing cabinet Save icon, and you should then be able to search for Cura from the Chrome OS launcher.
![Using menulibre to create the launcher shortcut](VTISYOKHO.png)
![Screenshot demoing the use of 'menulibre' to create the launcher shortcut](VTISYOKHO.png)
![Ultimaker Cura](f8nRJcyI6.png)
![Screenshot of Ultimake Cura software](f8nRJcyI6.png)
From there, just import the STL mesh, configure the appropriate settings, slice, and save the resulting GCODE. You can then just upload the GCODE straight to The Spaghetti Detective and kick off the print.
![Successful print, designed and sliced on Chrome OS!](2g57odtq2.jpeg)
![A 3d-printed adapter for mounting a rear reflector on a bicycle, designed, sliced, and printed from a Chromebook](2g57odtq2.jpeg)
Nice!

View file

@ -1,5 +1,5 @@
---
series: Tips
categories: Tips
date: "2020-09-24T08:34:30Z"
thumbnail: fmLDUWjia.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-06-01T08:34:30Z"
thumbnail: -Fuvz-GmF.png
usePageBundles: true
@ -11,7 +11,7 @@ tags:
title: Adding VM Notes and Custom Attributes with vRA8
---
*In [past posts](/series/vra8), I started by [creating a basic deployment infrastructure](/vra8-custom-provisioning-part-one) in Cloud Assembly and using tags to group those resources. I then [wrote an integration](/integrating-phpipam-with-vrealize-automation-8) to let vRA8 use phpIPAM for static address assignments. I [implemented a vRO workflow](/vra8-custom-provisioning-part-two) for generating unique VM names which fit an organization's established naming standard, and then [extended the workflow](/vra8-custom-provisioning-part-three) to avoid any naming conflicts in Active Directory and DNS. And, finally, I [created an intelligent provisioning request form in Service Broker](/vra8-custom-provisioning-part-four) to make it easy for users to get the servers they need. That's got the core functionality pretty well sorted, so moving forward I'll be detailing additions that enable new capabilities and enhance the experience.*
*In [past posts](/categories/vmware), I started by [creating a basic deployment infrastructure](/vra8-custom-provisioning-part-one) in Cloud Assembly and using tags to group those resources. I then [wrote an integration](/integrating-phpipam-with-vrealize-automation-8) to let vRA8 use phpIPAM for static address assignments. I [implemented a vRO workflow](/vra8-custom-provisioning-part-two) for generating unique VM names which fit an organization's established naming standard, and then [extended the workflow](/vra8-custom-provisioning-part-three) to avoid any naming conflicts in Active Directory and DNS. And, finally, I [created an intelligent provisioning request form in Service Broker](/vra8-custom-provisioning-part-four) to make it easy for users to get the servers they need. That's got the core functionality pretty well sorted, so moving forward I'll be detailing additions that enable new capabilities and enhance the experience.*
In this post, I'll describe how to get certain details from the Service Broker request form and into the VM's properties in vCenter. The obvious application of this is adding descriptive notes so I can remember what purpose a VM serves, but I will also be using [Custom Attributes](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vcenterhost.doc/GUID-73606C4C-763C-4E27-A1DA-032E4C46219D.html) to store the server's Point of Contact information and a record of which ticketing system request resulted in the server's creation.

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Self-Hosting
date: "2021-05-27T08:34:30Z"
thumbnail: HRRpFOKuN.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Code
date: "2020-11-24T08:34:30Z"
lastmod: "2021-03-12"
thumbnail: Ki7jo65t3.png

View file

@ -1,5 +1,5 @@
---
series: Code
categories: Code
date: "2021-04-29T08:34:30Z"
usePageBundles: true
thumbnail: 20210723-script.png
@ -11,7 +11,7 @@ title: Automatic unattended expansion of Linux root LVM volume to fill disk
toc: false
---
While working on my [vRealize Automation 8 project](/series/vra8), I wanted to let users specify how large a VM's system drive should be and have vRA apply that without any further user intervention. For instance, if the template has a 60GB C: drive and the user specifies that they want it to be 80GB, vRA will embiggen the new VM's VMDK to 80GB and then expand the guest file system to fill up the new free space.
While working on my [vRealize Automation 8 project](/categories/vmware), I wanted to let users specify how large a VM's system drive should be and have vRA apply that without any further user intervention. For instance, if the template has a 60GB C: drive and the user specifies that they want it to be 80GB, vRA will embiggen the new VM's VMDK to 80GB and then expand the guest file system to fill up the new free space.
I'll get into the details of how that's implemented from the vRA side #soon, but first I needed to come up with simple scripts to extend the guest file system to fill the disk.

View file

@ -1,14 +1,14 @@
---
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
comments: true
thumbnail: thumbnail.png
series: Projects
categories: Self-Hosting
tags:
- api
- automation
@ -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

@ -1,5 +1,5 @@
---
series: Projects
categories: Self-Hosting
date: "2018-09-26T08:34:30Z"
lastmod: "2022-03-06"
thumbnail: i0UKdXleC.png

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "code.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Code
categories: Code
tags:
- vmware
- powercli

View file

@ -1,5 +1,5 @@
---
series: Tips
categories: ChromeOS
date: "2020-12-23T08:34:30Z"
thumbnail: -lp1-DGiM.png
usePageBundles: true

View file

@ -14,7 +14,7 @@ usePageBundles: true
# thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8, K8s on vSphere
categories: Tips # Projects, Code, vRA8, K8s on vSphere
tags:
- linux
- shell

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Self-Hosting
date: "2021-10-28T00:00:00Z"
thumbnail: 20211028_wireguard_in_the_cloud.jpg
usePageBundles: true

View file

@ -1,7 +1,7 @@
---
title: "Create Virtual Machines on a Chromebook with HashiCorp Vagrant" # Title of the blog post.
date: 2023-02-20 # Date of post creation.
lastmod: 2023-02-25
lastmod: 2024-01-17
description: "Pairing the powerful Linux Development Environment on modern Chromebooks with HashiCorp Vagrant to create and manage local virtual machines for development and testing" # Description used for search engine.
featured: true # Sets if post is a featured post, making appear on the home page side bar.
draft: false # Sets whether to render this page. Draft of true will not be rendered.
@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects
categories: ChromeOS
tags:
- linux
- chromeos
@ -57,6 +57,16 @@ echo "remember_owner = 0" | sudo tee -a /etc/libvirt/qemu.conf # [tl! .cmd:1]
sudo systemctl restart libvirtd
```
{{% notice note "Update 2024-01-17" %}}
There seems to be an [issue with libvirt in LXC containers on Debian Bookworm](https://gitlab.com/libvirt/libvirt/-/issues/556), which explains why I was getting errors on `vagrant up` after updating my Crostini environment.
The workaround is to add another line to `qemu.conf`:
```shell
echo "namespaces = []" | sudo tee -a /etc/libvirt/qemu.conf # [tl! .cmd:1]
sudo systemctl restart libvirtd
```
{{% /notice %}}
I'm also going to use `rsync` to share a [synced folder](https://developer.hashicorp.com/vagrant/docs/synced-folders/basic_usage) between the host and the VM guest so I'll need to make sure that's installed too:
```shell
sudo apt install rsync # [tl! .cmd]

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-08-13T00:00:00Z"
lastmod: "2022-01-18"
usePageBundles: true

View file

@ -5,7 +5,7 @@ description: "I moved my homelab from VMware vSphere to Proxmox VE, and my only
featured: false
toc: true
comments: true
series: Tips # Projects, Code
categories: Tips # Projects, Code
tags:
- homelab
- linux

View file

@ -2,6 +2,7 @@
date: "2020-09-22T08:34:30Z"
thumbnail: 8p-PSHx1R.png
usePageBundles: true
categories: Tips
tags:
- docker
- windows

View file

@ -6,7 +6,7 @@ description: "Deploying and configuring a self-hosted pub-sub notification handl
featured: false
toc: true
comments: true
series: Projects
categories: Self-Hosting
tags:
- android
- api

View file

@ -0,0 +1,46 @@
---
title: "Enabling FIPS Compliance Fixes Aria Lifecycle 8.14"
date: 2024-01-19
# lastmod: 2024-01-19
description: "Never in my life have I seen enabling FIPS *fix* a problem - until now."
featured: false
comments: true
categories: VMware
tags:
- vmware
---
This week, VMware posted [VMSA-2024-0001](https://www.vmware.com/security/advisories/VMSA-2024-0001.html) which details a critical (9.9/10) vulnerability in <s>vRealize</s> *Aria* Automation. While working to get our environment patched, I ran into an interesting error on our Aria Lifecycle appliance:
```log
Error Code: LCMVRAVACONFIG590024
VMware Aria Automation hostname is not valid or unable to run the product specific commands via SSH on the host. Check if VMware Aria Automation is up and running.
VMware Aria Automation hostname is not valid or unable to run the product specific commands via SSH on the host. Check if VMware Aria Automation is up and running.
com.vmware.vrealize.lcm.drivers.vra80.exception.VraVaProductNotFoundException: Either provided hostname: <VMwareAriaAutomationFQDN> is not a valid VMware Aria Automation hostname or unable to run the product specific commands via SSH on the host.
at com.vmware.vrealize.lcm.drivers.vra80.helpers.VraPreludeInstallHelper.getVraFullVersion(VraPreludeInstallHelper.java:970)
at com.vmware.vrealize.lcm.drivers.vra80.helpers.VraPreludeInstallHelper.checkVraApplianceAndVersion(VraPreludeInstallHelper.java:978)
at com.vmware.vrealize.lcm.drivers.vra80.helpers.VraPreludeInstallHelper.getVraProductDetails(VraPreludeInstallHelper.java:754)
at com.vmware.vrealize.lcm.plugin.core.vra80.task.VraVaImportEnvironmentTask.execute(VraVaImportEnvironmentTask.java:145)
at com.vmware.vrealize.lcm.platform.automata.service.Task.retry(Task.java:158)
at com.vmware.vrealize.lcm.automata.core.TaskThread.run(TaskThread.java:60)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
```
Digging further into the appliance logs revealed some more details:
```log
Session.connect: java.security.spec.InvalidKeySpecException: key spec not recognized
```
That seems like a much more insightful error than "the hostname is not valid, dummy."
Anyhoo, searching for the error took me to a VMware KB on the subject:
- [VMware Aria Suite Lifecycle 8.14 Patch 1 Day 2 operations fail for VMware Aria Automation with error code LCMVRAVACONFIG590024 (96243)](https://kb.vmware.com/s/article/96243)
> After applying VMware Aria Suite Lifecycle 8.14 Patch 1, you may encounter deployment and day-2 operation failures, attributed to the elimination of weak algorithms in Suite Lifecycle. To prevent such issues, it is recommended to either turn on FIPS in VMware Aria Suite Lifecycle or implement the specified workarounds on other VMware Aria Products, as outlined in the article Steps for Removing SHA1 weak Algorithms/Ciphers from all VMware Aria Products.
That's right. According to the KB, the solution for the untrusted encryption algorithms is to *enable* FIPS compliance. I was skeptical: I've never seen FIPS enforcement fix problems, it always causes them.
But I gave it a shot, and *holy crap it actually worked!* Enabling FIPS compliance on the Aria Lifecycle appliance got things going again.
I feel like I've seen everything now.

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "tanzu-completion.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips
categories: VMware
tags:
- vmware
- linux

View file

@ -14,7 +14,7 @@ featureImage: "quartz64.jpg" # Sets featured image on blog post.
thumbnail: "quartz64.jpg" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects
categories: VMware
tags:
- vmware
- linux

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Self-Hosting
date: "2021-06-28T00:00:00Z"
thumbnail: 2xe34VJym.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: Tips
categories: Tips
date: "2020-09-13T08:34:30Z"
usePageBundles: true
tags:

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-11-05T00:00:00Z"
thumbnail: 20211105_ssc_403.png
usePageBundles: true

View file

@ -2,6 +2,7 @@
date: "2020-10-07T08:34:30Z"
thumbnail: MnmMuA0HC.png
usePageBundles: true
categories: Tips
tags:
- windows
- linux

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Self-Hosting
date: "2021-08-20T00:00:00Z"
lastmod: 2022-02-03
usePageBundles: true

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: vRA8 # Projects, Code, vRA8
categories: VMware # Projects, Code, vRA8
tags:
- vmware
- vra
@ -312,7 +312,7 @@ This doesn't give me the *name* of the regions, but I could use the `_links.regi
You'll notice that HTTPie also prettifies the JSON response to make it easy for humans to parse. This is great for experimenting with requests against different API endpoints and getting a feel for what data can be found where. And firing off tests in HTTPie can be a lot quicker (and easier to format) than with other tools.
Now let's take what we've learned and see about implementing it as vRO actions.
[^pie]: ![](pie.gif)
[^pie]: ![GIF from Supernatural wherein Dean ogles some delicious pie.](pie.gif)
[^token]: Well, most of it.
[^foreshadowing]: That knowledge will come in handy later.
### vRealize Orchestrator actions

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "gitea-logo.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects
categories: Self-Hosting
tags:
- caddy
- linux

View file

@ -16,6 +16,7 @@ shareImage: "/hugo-logo-wide.png"
# shareImage: "/images/path/share.png" # Designate a separate image for social media sharing.
codeMaxLines: 10 # Override global value for how many lines within a code block before auto-collapsing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
categories: Backstage
tags:
- meta
- hugo

View file

@ -6,7 +6,7 @@ timeless: true
description: There are no dumb questions - but there are smarter (and dumber) ways to ask them.
featured: true
aliases: ["how2ask"]
series: Tips
categories: Tips
---
I spend a lot of my time and energy answering technical questions, both professionally and "for fun" as a way to scratch that troubleshooting itch. How a question is asked plays a big factor in how effectively I'll be able to answer it.

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-02-22T08:34:30Z"
lastmod: 2022-07-25
thumbnail: 7_QI-Ti8g.png

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-07-21T00:00:00Z"
thumbnail: 20210721-successful-ad_machine.png
usePageBundles: true

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "thumbnail.jpg" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: K8s on vSphere
categories: VMware
tags:
- vmware
- linux

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "ldaps_test.png" # Sets thumbnail image appearing inside card on homepage.
shareImage: "ldaps_test.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: K8s on vSphere
categories: VMware
tags:
- vmware
- kubernetes

View file

@ -14,7 +14,7 @@ featureImage: "tanzu.png" # Sets featured image on blog post.
thumbnail: "tanzu.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips
categories: VMware
tags:
- vmware
- kubernetes

View file

@ -1,5 +1,5 @@
---
series: Code
categories: Code
date: "2020-09-16T08:34:30Z"
thumbnail: LJOcy2oqc.png
usePageBundles: true

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "nessus_login.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8
categories: Self-Hosting
tags:
- vmware
- kubernetes

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-08-25T00:00:00Z"
usePageBundles: true
tags:

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "PowerCLI.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Code
categories: Code
tags:
- vmware
- powercli

View file

@ -14,7 +14,7 @@ usePageBundles: true
# thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Code
categories: Code
tags:
- powershell
- windows

View file

@ -1,5 +1,5 @@
---
series: Tips
categories: VMware
date: "2021-01-30T08:34:30Z"
thumbnail: XTaU9VDy8.png
usePageBundles: true

View file

@ -14,7 +14,7 @@ usePageBundles: true
# thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8, K8s on vSphere
categories: VMware # Projects, Code, vRA8, K8s on vSphere
tags:
- vmware
- powershell

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
categories: Self-Hosting
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

@ -1,5 +1,5 @@
---
series: Tips
categories: Backstage
date: "2021-07-24T16:46:00Z"
thumbnail: 20210724-series-navigation.png
usePageBundles: true
@ -9,12 +9,12 @@ tags:
title: Recreating Hashnode Series (Categories) in Jekyll on GitHub Pages
---
I recently [migrated this site](/virtually-potato-migrated-to-github-pages) from Hashnode to GitHub Pages, and I'm really getting into the flexibility and control that managing the content through Jekyll provides. So, naturally, after finalizing the move I got to work recreating Hashnode's "Series" feature, which lets you group posts together and highlight them as a collection. One of the things I liked about the Series setup was that I could control the order of the collected posts: my posts about [building out the vRA environment in my homelab](/series/vra8) are probably best consumed in chronological order (oldest to newest) since the newer posts build upon the groundwork laid by the older ones, while posts about my [other one-off projects](/series/projects) could really be enjoyed in any order.
I recently [migrated this site](/virtually-potato-migrated-to-github-pages) from Hashnode to GitHub Pages, and I'm really getting into the flexibility and control that managing the content through Jekyll provides. So, naturally, after finalizing the move I got to work recreating Hashnode's "Series" feature, which lets you group posts together and highlight them as a collection. One of the things I liked about the Series setup was that I could control the order of the collected posts: my posts about [building out the vRA environment in my homelab](/categories/vmware) are probably best consumed in chronological order (oldest to newest) since the newer posts build upon the groundwork laid by the older ones, while posts about my [other one-off projects](/categories/self-hosting) could really be enjoyed in any order.
I quickly realized that if I were hosting this pretty much anywhere *other* than GitHub Pages I could simply leverage the [`jekyll-archives`](https://github.com/jekyll/jekyll-archives) plugin to manage this for me - but, alas, that's not one of the [plugins supported by the platform](https://pages.github.com/versions/). I needed to come up with my own solution, and being still quite new to Jekyll (and this whole website design thing in general) it took me a bit of fumbling to get it right.
### Reviewing the theme-provided option
The Jekyll theme I'm using ([Minimal Mistakes](https://github.com/mmistakes/minimal-mistakes)) comes with [built-in support](https://mmistakes.github.io/mm-github-pages-starter/categories/) for a [category archive page](/series), which (like the [tags page](/tags)) displays all the categorized posts on a single page. Links at the top will let you jump to an appropriate anchor to start viewing the selected category, but it's not really an elegant way to display a single category.
The Jekyll theme I'm using ([Minimal Mistakes](https://github.com/mmistakes/minimal-mistakes)) comes with [built-in support](https://mmistakes.github.io/mm-github-pages-starter/categories/) for a [category archive page](/categories), which (like the [tags page](/tags)) displays all the categorized posts on a single page. Links at the top will let you jump to an appropriate anchor to start viewing the selected category, but it's not really an elegant way to display a single category.
![Posts by category](20210724-posts-by-category.png)
It's a start, though, so I took a few minutes to check out how it's being generated. The category archive page lives at [`_pages/category-archive.md`](https://raw.githubusercontent.com/mmistakes/mm-github-pages-starter/master/_pages/category-archive.md):
@ -144,7 +144,7 @@ Since I can't use a plugin to automatically generate pages for each series, I'll
---
title: "Adventures in vRealize Automation 8"
layout: series
permalink: "/series/vra8"
permalink: "/categories/vmware"
tag: vRA8
sort_order: reverse
author_profile: true
@ -155,9 +155,9 @@ header:
*Follow along as I create a flexible VMware vRealize Automation 8 environment for provisioning virtual machines - all from the comfort of my Intel NUC homelab.*
```
You can see that this page is referencing the series layout I just created, and it's going to live at `http://localhost/series/vra8` - precisely where this series was on Hashnode. I've tagged it with the category I want to feature on this page, and specified that the posts will be sorted in reverse order so that anyone reading through the series will start at the beginning (I hear it's a very good place to start). I also added a teaser image which will be displayed when I link to the series from elsewhere. And I included a quick little italicized blurb to tell readers what the series is about.
You can see that this page is referencing the series layout I just created, and it's going to live at `http://localhost/categories/vmware` - precisely where this series was on Hashnode. I've tagged it with the category I want to feature on this page, and specified that the posts will be sorted in reverse order so that anyone reading through the series will start at the beginning (I hear it's a very good place to start). I also added a teaser image which will be displayed when I link to the series from elsewhere. And I included a quick little italicized blurb to tell readers what the series is about.
Check it out [here](/series/vra8):
Check it out [here](/categories/vmware):
![vRA8 series](20210724-vra8-series.png)
The other series pages will be basically the same, just without the reverse sort directive. Here's `_pages/series-tips.md`:
@ -202,7 +202,7 @@ author_profile: true
```
### Fixing category links in posts
The bottom of each post has a section which lists the tags and categories to which it belongs. Right now, those are still pointing to the category archive page (`/series/#vra8`) instead of the series feature pages I created (`/series/vra8`).
The bottom of each post has a section which lists the tags and categories to which it belongs. Right now, those are still pointing to the category archive page (`/series/#vra8`) instead of the series feature pages I created (`/categories/vmware`).
![Old category link](20210724-old-category-link.png)
That *works* but I'd rather it reference the fancy new pages I created. Tracking down where to make that change was a bit of a journey.
@ -245,7 +245,7 @@ Okay, it looks like [`_include/category-list.html`](https://github.com/mmistakes
{% assign categories_sorted = page.categories | sort_natural %}
<p class="page__taxonomy">
<strong><i class="fas fa-fw fa-folder-open" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].categories_label | default: "series:" }} </strong>
<strong><i class="fas fa-fw fa-folder-open" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].categories_label | default: "categories:" }} </strong>
<span itemprop="keywords">
{% for category_word in categories_sorted %}
<a href="{{ category_word | slugify | prepend: path_type | prepend: site.category_archive.path | relative_url }}" class="page__taxonomy-item p-category" rel="tag">{{ category_word }}</a>{% unless forloop.last %}<span class="sep">, </span>{% endunless %}
@ -283,9 +283,9 @@ And, finally, I'll want to update the navigation links at the top of each page t
# torchlight! {"lineNumbers": true}
main:
- title: "vRealize Automation 8"
url: /series/vra8
url: /categories/vmware
- title: "Projects"
url: /series/projects
url: /categories/self-hosting
- title: "Code"
url: /series/code
- title: "Tips & Tricks"

View file

@ -14,7 +14,7 @@ featureImage: "basic-architecture.png" # Sets featured image on blog post.
thumbnail: "basic-architecture.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8
categories: VMware # Projects, Code, vRA8
tags:
- vmware
- vsphere
@ -36,7 +36,7 @@ Fortunately there's a somewhat-hidden way to disable (and re-enable) vCLS on a p
Disabling vCLS will break DRS, and could have other unintended side effects. Don't do this in prod if you can avoid it.
{{% /notice %}}
[^off-and-on]: ![](off-and-on.gif)
[^off-and-on]: ![GIF from The IT Crowd: "Have you tried turning it off and back on again?"](off-and-on.gif)
### Find the cluster's domain ID
It starts with determining the affected cluster's domain ID, which is very easy to do once you know where to look. Simply browse to the cluster object in the vSphere inventory, and look at the URL:

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-09-03T00:00:00Z"
thumbnail: 20210903_action_run_success.png
usePageBundles: true
@ -11,7 +11,7 @@ tags:
- vmware
title: Run scripts in guest OS with vRA ABX Actions
---
Thus far in my [vRealize Automation project](/series/vra8), I've primarily been handing the payload over to vRealize Orchestrator to do the heavy lifting on the back end. This approach works really well for complex multi-part workflows (like when [generating unique hostnames](/vra8-custom-provisioning-part-two#the-vro-workflow)), but it may be overkill for more linear tasks (such as just running some simple commands inside of a deployed guest OS). In this post, I'll explore how I use [vRA Action Based eXtensibility (ABX)](https://blogs.vmware.com/management/2020/09/vra-abx-flow.html) to do just that.
Thus far in my [vRealize Automation project](/categories/vmware), I've primarily been handing the payload over to vRealize Orchestrator to do the heavy lifting on the back end. This approach works really well for complex multi-part workflows (like when [generating unique hostnames](/vra8-custom-provisioning-part-two#the-vro-workflow)), but it may be overkill for more linear tasks (such as just running some simple commands inside of a deployed guest OS). In this post, I'll explore how I use [vRA Action Based eXtensibility (ABX)](https://blogs.vmware.com/management/2020/09/vra-abx-flow.html) to do just that.
### The Goal
My ABX action is going to use PowerCLI to perform a few steps inside a deployed guest OS (Windows-only for this demonstration):

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: Code
date: "2020-11-14T08:34:30Z"
thumbnail: aeIOr8w6k.png
usePageBundles: true

View file

@ -6,7 +6,7 @@ description: "A hasty Salt state to deploy netdata monitoring and publish it int
featured: false
toc: true
comments: true
series: Code
categories: Code
tags:
- homelab
- iac

View file

@ -15,7 +15,7 @@ thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homep
# shareImage: "/images/path/share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
codeMaxLines: 30
series: Code
categories: Backstage
tags:
- hugo
- meta

View file

@ -1,5 +1,5 @@
---
series: Code
categories: Backstage
date: "2021-07-19T16:03:30Z"
usePageBundles: true
tags:

View file

@ -14,7 +14,7 @@ featureImageAlt: 'Tailscale Logo' # Alternative text for featured image.
thumbnail: "Tailscale-AppIcon.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects
categories: Self-Hosting
tags:
- vpn
- wireguard

View file

@ -1,5 +1,5 @@
---
series: Projects
categories: ChromeOS
date: "2020-10-27T08:34:30Z"
lastmod: "2021-05-20"
thumbnail: XtmaR9Z0J.png

View file

@ -4,6 +4,7 @@ thumbnail: P-x5qEg_9.jpeg
usePageBundles: true
tags:
- chromeos
categories: ChromeOS
title: 'Showdown: Lenovo Chromebook Duet vs. Google Pixel Slate'
---

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "snikket.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects
categories: Self-Hosting
tags:
- linux
- cloud

View file

@ -6,7 +6,7 @@ description: "Syntax highlighting powered by the Torchlight.dev API makes it eas
featured: false
toc: true
comments: true
series: Projects # Projects, Code
categories: Backstage
tags:
- javascript
- hugo
@ -536,7 +536,7 @@ Serving HTTP on 0.0.0.0 port 1313 (http://0.0.0.0:1313/) ... # [tl! focus:1]
#### Netlify
Setting up Netlify to leverage the Torchlight API is kind of similar. I'll start with logging in to the [Netlify dashboard](https://app.netlify.com) and navigating to **Site Configuration > Environment Variables**. There, I'll click on **Add a variable > Add a ingle variable**. I'll give the new variable a key of `TORCHLIGHT_TOKEN` and set its value to the token I obtained earlier.
![](netlify-env-var.png)
![Screenshot showing the creation of the 'TORCHLIGHT_TOKEN' variable in Netlify](netlify-env-var.png)
Once that's done, I edit the `netlify.toml` file at the root of my site repo to alter the build commands:
```toml

View file

@ -6,7 +6,7 @@ description: "Quick notes on using `systemctl edit` to override a systemd servic
featured: false
toc: false
comments: true
series: Tips # Projects, Code
categories: Tips # Projects, Code
tags:
- crostini
- linux

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "golinks.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Projects # Projects, Code, vRA8, K8s on vSphere
categories: Self-Hosting # Projects, Code, vRA8, K8s on vSphere
tags:
- docker
- vpn

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "Tailscale-AppIcon.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8, K8s on vSphere
categories: Tips # Projects, Code, vRA8, K8s on vSphere
tags:
- vmware
- linux

View file

@ -6,7 +6,7 @@ description: "Using Docker Compose to deploy containerized applications and make
featured: false
toc: true
comments: true
series: Projects
categories: Self-Hosting
tags:
- containers
- docker

View file

@ -6,7 +6,7 @@ description: "Exploring some of my favorite Tailscale addon features: SSH, Serve
featured: false
toc: true
comments: true
series: Tips # Projects, Code
categories: Tips # Projects, Code
tags:
- homelab
- networking

View file

@ -14,7 +14,7 @@ usePageBundles: true
thumbnail: "tanzu_community_edition.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: K8s on vSphere
categories: VMware
tags:
- vmware
- linux
@ -983,7 +983,8 @@ I'll define the new subnet as `192.168.1.0/24`. Once I enable the option to *Che
![A new (but empty) subnet](new_subnet_pre_scan.png)
It shows the scanner associated with the subnet, but no data yet. I'll need to wait a few minutes for the first scan to kick off (at the five-minute interval I defined in the configuration).
![](five_minutes.gif)
![GIF which says 'Five Minutes Later!'](five_minutes.gif)
![Newly discovered IPs!](newly-discovered_IPs.png)
Woah, it actually works!

View file

@ -14,7 +14,7 @@ featureImage: "esxi8.png" # Sets featured image on blog post.
# thumbnail: "thumbnail.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8
categories: VMware # Projects, Code, vRA8
tags:
- vmware
- homelab

View file

@ -1,5 +1,5 @@
---
series: Code
categories: Code
date: "2021-04-29T08:34:30Z"
usePageBundles: true
tags:
@ -9,7 +9,7 @@ title: Using PowerShell and a Scheduled Task to apply Windows Updates
toc: false
---
In the same vein as [my script to automagically resize a Linux LVM volume to use up free space on a disk](/automatic-unattended-expansion-of-linux-root-lvm-volume-to-fill-disk), I wanted a way to automatically apply Windows updates for servers deployed by [my vRealize Automation environment](/series/vra8). I'm only really concerned with Windows Server 2019, which includes the [built-in Windows Update Provider PowerShell module](https://4sysops.com/archives/scan-download-and-install-windows-updates-with-powershell/). So this could be as simple as `Install-WUUpdates -Updates (Start-WUScan)` to scan for and install any available updates.
In the same vein as [my script to automagically resize a Linux LVM volume to use up free space on a disk](/automatic-unattended-expansion-of-linux-root-lvm-volume-to-fill-disk), I wanted a way to automatically apply Windows updates for servers deployed by [my vRealize Automation environment](/categories/vmware). I'm only really concerned with Windows Server 2019, which includes the [built-in Windows Update Provider PowerShell module](https://4sysops.com/archives/scan-download-and-install-windows-updates-with-powershell/). So this could be as simple as `Install-WUUpdates -Updates (Start-WUScan)` to scan for and install any available updates.
Unfortunately, I found that this approach can take a long time to run and often exceeded the timeout limits imposed upon my ABX script, causing the PowerShell session to end and terminating the update process. I really needed a way to do this without requiring a persistent session.

View file

@ -1,5 +1,5 @@
---
series: Tips
categories: Tips
date: "2021-02-18T08:34:30Z"
thumbnail: PPZu_UOGO.png
usePageBundles: true

View file

@ -14,7 +14,7 @@ featureImage: "vdt.png" # Sets featured image on blog post.
thumbnail: "pulse2.png" # Sets thumbnail image appearing inside card on homepage.
# shareImage: "share.png" # Designate a separate image for social media sharing.
codeLineNumbers: false # Override global value for showing of line numbers within code block.
series: Tips # Projects, Code, vRA8
categories: VMware # Projects, Code, vRA8
tags:
- vmware
- vsphere

View file

@ -2,6 +2,7 @@
date: "2021-07-20T22:20:00Z"
thumbnail: 20210720-jekyll.png
usePageBundles: true
categories: Backstage
tags:
- linux
- meta
@ -71,4 +72,4 @@ And there it is!
### `git push` time
Alright that's enough rambling for now. I'm very happy with this new setup, particularly with the automatically-generated Table of Contents to help folks navigate some of my longer posts. (I can't believe I was having to piece those together manually in this blog's previous iteration!)
I'll continue to make some additional tweaks in the coming weeks but for now I'll `git push` this post and get back to documenting my never-ending [vRA project](/series/vra8).
I'll continue to make some additional tweaks in the coming weeks but for now I'll `git push` this post and get back to documenting my never-ending [vRA project](/categories/vmware).

View file

@ -7,6 +7,7 @@ draft: false
description: "This blog has migrated from virtuallypotato.com to runtimeterror.dev."
toc: false
comments: true
categories: Backstage
tags:
- meta
---
@ -19,7 +20,7 @@ ln -s virtuallypotato.com runtimeterror.dev
If you've noticed that things look a bit different around here, you might *also* have noticed that my posts about VMware products had become less and less frequent over the past year or so. That wasn't intentional, but a side-effect of some shifting priorities with a new position at work. I'm no longer on the team responsible for our VMware environment and am now more focused on cloud-native technologies and open-source DevOps solutions. The new role keeps me pretty busy, and I'm using what free time I have to learn more about and experiment with the technologies I use at work.
That (unfortunately) means that I won't be posting much (if at all) about VMware-related things (including the [vRA8 series of posts](/series/vra8/))[^vra8] going forward. Instead, expect to see more posts about things like [containers](/tags/containers/), [infrastructure-as-code](/tags/iac/), [self-hosting](/tags/selfhosting/), and [miscellaneous tech projects](/series/projects/) that I play with.
That (unfortunately) means that I won't be posting much (if at all) about VMware-related things (including the [vRA8 series of posts](/categories/vmware/))[^vra8] going forward. Instead, expect to see more posts about things like [containers](/tags/containers/), [infrastructure-as-code](/tags/iac/), [self-hosting](/tags/selfhosting/), and [miscellaneous tech projects](/categories/self-hosting/) that I play with.
I decided to migrate, rebrand, and re-theme my blog to reflect this change in focus. virtuallypotato used a [theme heavily inspired by VMware design language](https://github.com/chipzoller/hugo-clarity), and I don't think it's a great fit for the current (and future) content anymore. That theme is also very feature-rich which provides a lot of capability out of the box but makes it a bit tricky to modify (and maintain) my personal tweaks. The new runtimeterror[^pun] site uses a [more minimal theme](https://github.com/joeroe/risotto) which takes cues from terminals and markdown formatting. It's also simpler and thus easier for me to tweak. I've done a lot of that already and anticipating doing a bit more in the coming weeks, but I wanted to go ahead and make this thing "live" for now.

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-02-05T08:34:30Z"
thumbnail: SIDah-Lag.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-05-20T08:34:30Z"
thumbnail: wl-WPQpEl.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-05-18T08:34:30Z"
lastmod: "2021-05-20"
thumbnail: hFPeakMxn.png

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-03-29T08:34:30Z"
thumbnail: VZaK4btzl.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-04-19T08:34:30Z"
thumbnail: K6vcxpDj8.png
usePageBundles: true

View file

@ -1,5 +1,5 @@
---
series: vRA8
categories: VMware
date: "2021-04-02T08:34:30Z"
lastmod: "2022-03-23"
thumbnail: HXrAMJrH.png
@ -331,9 +331,9 @@ I'll do that with another scriptable task element, named `Apply new names`, whic
{{% notice note "Binding a workflow output" %}}
To easily create a new workflow output and bind it to a task's output, click the task's **Add New** option like usual:
![](add_new.png)
![Screenshot showing the creation of a new output](add_new.png)
Select **Output** at the top of the *New Variable* dialog and the complete the form with the other required details:
![](new_output_parameter.png)
![Screenshot showing the new output parameter with 'Name: resourceNames' and 'Type: string'](new_output_parameter.png)
{{% /notice %}}

3
content/search/_index.md Normal file
View file

@ -0,0 +1,3 @@
---
title: Search Results Page
---

Some files were not shown because too many files have changed in this diff Show more