<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Technical on Golder.org</title><link>https://golder.org/categories/technical/</link><description>Recent content in Technical on Golder.org</description><generator>Hugo</generator><language>en-gb</language><lastBuildDate>Wed, 02 Jul 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://golder.org/categories/technical/index.xml" rel="self" type="application/rss+xml"/><item><title>How This Site is Built: A Modern DevOps Pipeline</title><link>https://golder.org/posts/2025-07-02-how-this-site-is-built/</link><pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate><guid>https://golder.org/posts/2025-07-02-how-this-site-is-built/</guid><description>&lt;h2 id="architecture-overview">Architecture Overview&lt;/h2>
&lt;p>The site follows a GitOps approach, with every component self-hosted across multiple geographic regions:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Source Control&lt;/strong>: Self-hosted Gitea Git repository&lt;/li>
&lt;li>&lt;strong>Static Site Generator&lt;/strong>: Hugo with custom theme&lt;/li>
&lt;li>&lt;strong>Build Pipeline&lt;/strong>: Argo Workflows&lt;/li>
&lt;li>&lt;strong>Container Registry&lt;/strong>: Harbor&lt;/li>
&lt;li>&lt;strong>Orchestration&lt;/strong>: Kubernetes clusters across multiple regions&lt;/li>
&lt;li>&lt;strong>Deployment&lt;/strong>: Flux GitOps operator&lt;/li>
&lt;li>&lt;strong>Networking&lt;/strong>: Home networks with VPN interconnects&lt;/li>
&lt;/ul>
&lt;h2 id="static-site-generation-with-hugo">Static Site Generation with Hugo&lt;/h2>
&lt;p>The foundation is &lt;a href="https://gohugo.io/">Hugo&lt;/a>, a fast static site generator written in Go. Hugo was chosen for several key advantages:&lt;/p></description></item><item><title>Packer node builder</title><link>https://golder.org/posts/packer-node-builder/</link><pubDate>Thu, 28 Nov 2024 00:00:00 +0000</pubDate><guid>https://golder.org/posts/packer-node-builder/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>In this article I will discuss my &lt;code>k8s-node-packer&lt;/code> folder, which contains an environment I use to build fresh VM images which I then use to deploy VMs on various bare metal hosts running &lt;code>libvirtd&lt;/code>.&lt;/p>
&lt;h1 id="getting-started">Getting started&lt;/h1>
&lt;!-- raw HTML omitted -->
&lt;!-- raw HTML omitted -->
&lt;p>You will need a recent version of Packer. To summarise &lt;a href="https://developer.hashicorp.com/packer/tutorials/docker-get-started/get-started-install-cli">the installation&lt;/a> for Ubuntu&amp;hellip;&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt-add-repository &lt;span style="color:#e6db74">&amp;#34;deb [arch=amd64] https://apt.releases.hashicorp.com &lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>lsb_release -cs&lt;span style="color:#66d9ef">)&lt;/span>&lt;span style="color:#e6db74"> main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt-get update &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> sudo apt-get install packer
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Ensure some tools that will be needed are installed:&lt;/p></description></item><item><title>Enable Kubernetes auth on Hashicorp Vault</title><link>https://golder.org/posts/enable-kubernetes-auth-on-hashicorp-vault/</link><pubDate>Wed, 24 Jan 2024 00:00:00 +0000</pubDate><guid>https://golder.org/posts/enable-kubernetes-auth-on-hashicorp-vault/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>In this post I describe the steps taken to enable a Kubernetes authentication backend for Hashicorp Vault and configure a PKI role for &lt;a href="https://cert-manager.io">cert-manager&lt;/a> to use to issue certificate signing requests.&lt;/p>
&lt;h1 id="process">Process&lt;/h1>
&lt;p>Start by enabling the auth method.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>vault auth enable --path&lt;span style="color:#f92672">=&lt;/span>k8s-yourdomain-production kubernetes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You should see:&lt;/p>
&lt;pre tabindex="0">&lt;code>Success! Enabled kubernetes auth method at: k8s-yourdomain-production/
&lt;/code>&lt;/pre>&lt;p>The cluster needs a &amp;rsquo;token reviewer&amp;rsquo; service account that Vault can use to validate tokens presented by clients that claim to have been authenticated by the cluster.&lt;/p></description></item><item><title>Granting TLS user access to a Kubernetes Cluster</title><link>https://golder.org/posts/granting-tls-user-access-to-k8s-cluster/</link><pubDate>Tue, 23 Jan 2024 00:00:00 +0000</pubDate><guid>https://golder.org/posts/granting-tls-user-access-to-k8s-cluster/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>I run up clusters with &lt;a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/">kubeadm&lt;/a>. Once a cluster is up and running, it can be enticing to just copy the &lt;code>/etc/kubernetes/admin.conf&lt;/code> credentials down to your local &lt;code>.kube/config&lt;/code> file and crack on. However, as you can guess, that&amp;rsquo;s not best practice, especially when working in teams.&lt;/p>
&lt;p>In order for audit logs to contain more specific details about which user performed which actions, and for general access control purposes, it helps a lot if users authenticate to clusters using their own credentials, not using shared credentials.&lt;/p></description></item><item><title>Granting an OIDC user access to a Kubernetes Cluster</title><link>https://golder.org/posts/granting-oidc-user-access-to-k8s-cluster/</link><pubDate>Sun, 21 Jan 2024 00:00:00 +0000</pubDate><guid>https://golder.org/posts/granting-oidc-user-access-to-k8s-cluster/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>I run up clusters with &lt;a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/">kubeadm&lt;/a>. Once a cluster is up and running, it can be enticing to just copy the &lt;code>/etc/kubernetes/admin.conf&lt;/code> credentials down to your local &lt;code>.kube/config&lt;/code> file and crack on. However, as you can guess, that&amp;rsquo;s not best practice, especially when working in teams.&lt;/p>
&lt;p>In order for audit logs to contain more specific details about which user performed which actions, and for general access control purposes, it helps a lot if users authenticate to clusters using their own credentials, not using shared credentials.&lt;/p></description></item><item><title>Adding new user to Redis Cluster</title><link>https://golder.org/posts/adding-new-user-to-redis-cluster/</link><pubDate>Thu, 04 Jan 2024 00:00:00 +0000</pubDate><guid>https://golder.org/posts/adding-new-user-to-redis-cluster/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>I run Redis Cluster in K8S. I need to manage client access for service accounts.&lt;/p>
&lt;p>Currently I do this manually using the following process.&lt;/p>
&lt;h1 id="connect-to-redis-cluster">Connect to Redis Cluster&lt;/h1>
&lt;p>Ensure your &lt;code>kubectl&lt;/code> is configured to use the appropriate cluster.&lt;/p>
&lt;pre tabindex="0">&lt;code>export IP=$(kubectl -n redis get svc redis-redis-cluster -ojsonpath=&amp;#39;{.spec.clusterIP}&amp;#39;)
export REDISCLI_AUTH=$(kubectl -n redis get secret --namespace &amp;#34;redis&amp;#34; redis -o jsonpath=&amp;#34;{.data.redis-password}&amp;#34; | base64 -d)
redis-cli -c -h $IP
&lt;/code>&lt;/pre>&lt;p>This should give you a &lt;code>redis-cli&lt;/code> prompt like the following:&lt;/p></description></item><item><title>Renumber cluster IP addresses</title><link>https://golder.org/posts/renumber-cluster-ip-addresses/</link><pubDate>Sun, 14 May 2023 00:00:00 +0000</pubDate><guid>https://golder.org/posts/renumber-cluster-ip-addresses/</guid><description>&lt;p>So, about a year after I &lt;a href="https://golder.org/rebuild-staging-cluster">redeployed my in-house staging cluster&lt;/a>, I&amp;rsquo;ve reached a point where I&amp;rsquo;m not happy about the addressing scheme I&amp;rsquo;ve used and how it affects the routing on my internal network. So this article is going to try to cover the steps I took to re-number the whole cluster, and put all the nodes into their own LAN segment too to physically segregate/isolate it&amp;rsquo;s traffic for performance and security reasons.&lt;/p></description></item><item><title>AWS SSO with Keycloak via SAML</title><link>https://golder.org/posts/aws-sso-with-keycloak-via-saml/</link><pubDate>Tue, 12 Jul 2022 00:00:00 +0000</pubDate><guid>https://golder.org/posts/aws-sso-with-keycloak-via-saml/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>&lt;a href="https://www.keycloak.org/">Keycloak&lt;/a> is a popular Java-based SAML/OpenID identity service provider application that can be self-hosted. It&amp;rsquo;s a very comprehensive package and widely supported, so even if it is a bit of a &amp;lsquo;heavyweight&amp;rsquo; (resource and time-wise) to set up, host and support, it&amp;rsquo;s a more sensible and mature solution to adopt for now, and there doesn&amp;rsquo;t seem to be anything that fits our needs yet in the wider open source world.&lt;/p></description></item><item><title>Migration from WordPress to Hugo</title><link>https://golder.org/posts/migration-from-wordpress-to-hugo/</link><pubDate>Fri, 08 Oct 2021 00:00:00 +0000</pubDate><guid>https://golder.org/posts/migration-from-wordpress-to-hugo/</guid><description>&lt;p>I have three public-facing sites which I&amp;rsquo;d rather not have to manage with WordPress any more. None of them do anything fancy. They&amp;rsquo;re just a collection of pages and posts. I span them up and started adding content in the days where WordPress was all the rage. Recently, I&amp;rsquo;ve been looking at ditching the WordPress hosting burden and converting them to simple, static websites. This will save me time and money in the long run.&lt;/p></description></item><item><title>Development Snapshot Download Service</title><link>https://golder.org/posts/development-snapshot-download-service/</link><pubDate>Sat, 06 Jun 2020 00:00:00 +0000</pubDate><guid>https://golder.org/posts/development-snapshot-download-service/</guid><description>&lt;p>Suppose the database or state data for your production application is being backed up in full on a regular basis, encrypted and stored on an object storage service, such as S3. Suppose the &lt;a href="https://github.com/rossigee/backups">backup tool&lt;/a> you are using keeps track of successful backups in some kind of lookup service, such as an ElasticSearch index, database, etcd cluster or whatever. Suppose one of your developers needs a recent database snapshot to seed their local development environment to develop a feature or reproduce a bug.&lt;/p></description></item><item><title>Azure CLI can't find antlr4 python library</title><link>https://golder.org/posts/azure-cli-cant-find-antlr4-python-library/</link><pubDate>Sat, 14 Mar 2020 00:00:00 +0000</pubDate><guid>https://golder.org/posts/azure-cli-cant-find-antlr4-python-library/</guid><description>&lt;p>I&amp;rsquo;m trying to run up a VM on Azure using CLI commands on my Ubuntu 20.04 (Focal) workstation. I&amp;rsquo;m following the &lt;a href="https://docs.microsoft.com/en-us/azure/virtual-machines/linux/quick-create-cli">official instructions&lt;/a>. On running the &lt;code>az vm create&lt;/code> command, I get the following error:&lt;/p>
&lt;p>The command failed with an unexpected error. Here is the traceback:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>No module named &lt;span style="color:#e6db74">&amp;#39;antlr4&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Traceback &lt;span style="color:#f92672">(&lt;/span>most recent call last&lt;span style="color:#f92672">)&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/knack/cli.py&amp;#34;&lt;/span>, line 206, in invoke
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cmd_result &lt;span style="color:#f92672">=&lt;/span> self.invocation.execute&lt;span style="color:#f92672">(&lt;/span>args&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/azure/cli/core/commands/__init__.py&amp;#34;&lt;/span>, line 528, in execute
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> self.commands_loader.load_arguments&lt;span style="color:#f92672">(&lt;/span>command&lt;span style="color:#f92672">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/azure/cli/core/__init__.py&amp;#34;&lt;/span>, line 300, in load_arguments
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> loader.load_arguments&lt;span style="color:#f92672">(&lt;/span>command&lt;span style="color:#f92672">)&lt;/span> &lt;span style="color:#75715e"># this adds entries to the argument registries&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/azure/cli/command_modules/vm/__init__.py&amp;#34;&lt;/span>, line 31, in load_arguments
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> from azure.cli.command_modules.vm._params import load_arguments
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/azure/cli/command_modules/vm/_params.py&amp;#34;&lt;/span>, line 31, in &amp;lt;module&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> from azure.cli.command_modules.monitor.actions import get_period_type
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> File &lt;span style="color:#e6db74">&amp;#34;/usr/lib/python3/dist-packages/azure/cli/command_modules/monitor/actions.py&amp;#34;&lt;/span>, line 7, in &amp;lt;module&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> import antlr4
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ModuleNotFoundError: No module named &lt;span style="color:#e6db74">&amp;#39;antlr4&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Looks like the Azure CLI packages for Ubuntu have a dependency on &amp;lsquo;antlr4&amp;rsquo; python library, and that isn&amp;rsquo;t being packaged or distributed upstream for whatever reason. In my case I&amp;rsquo;m using the Ubuntu Focal (20.04) series.&lt;/p></description></item><item><title>Dynamic DNS with external-dns</title><link>https://golder.org/posts/dynamic-dns-with-external-dns/</link><pubDate>Sat, 14 Mar 2020 00:00:00 +0000</pubDate><guid>https://golder.org/posts/dynamic-dns-with-external-dns/</guid><description>&lt;p>This came about from deciding to switch from Route53 to Cloudflare for DNS management. I&amp;rsquo;d already prepared the destination zones on Cloudflare but needed to update the mechanism I had in place that manages the DNS entry for the dynamic IP address. The mechanism was a NodeRED flow that made an API call directly to Route53. I&amp;rsquo;d need to do some research on how to do the same on Cloudflare.&lt;/p></description></item><item><title>Deploying OPNSense to no-name WAN router hardware</title><link>https://golder.org/posts/deploying-opnsense-to-no-name-wan-router-hardware/</link><pubDate>Fri, 10 Jan 2020 00:00:00 +0000</pubDate><guid>https://golder.org/posts/deploying-opnsense-to-no-name-wan-router-hardware/</guid><description>&lt;p>I live and work in a remote area, which does not have the most reliable power and internet connections. I&amp;rsquo;ve addressed the intermittent power outages by installing a battery back-up system which covers the server cabinet and various other circuits in the house. I&amp;rsquo;ve addressed the intermittent network issues by deploying a multi-WAN router, which is what this article is about.&lt;/p>
&lt;p>The WAN router also provides me with an extra NAT layer between my home/work network and the Internet, as you never know if you can trust ISP-provided routers. Additionally, it provides me and members of my team with OpenVPN access to resources on the network when we&amp;rsquo;re out and about, and much more besides.&lt;/p></description></item><item><title>Grant read-only rights to backup user for PostgreSQL</title><link>https://golder.org/posts/grant-read-only-rights-to-backup-user-for-postgresql/</link><pubDate>Mon, 29 Jul 2019 00:00:00 +0000</pubDate><guid>https://golder.org/posts/grant-read-only-rights-to-backup-user-for-postgresql/</guid><description>&lt;p>This one comes up quite regularly, so worthy of note. Whenever an application using Postgres changes or creates tables (i.e. Odoo module updates for example), those tables don&amp;rsquo;t seem to be accessible by the &lt;code>backups&lt;/code> user on the next backup run. This is the SQL I use to work around it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sql" data-lang="sql">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">GRANT&lt;/span> &lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#66d9ef">ON&lt;/span> &lt;span style="color:#66d9ef">ALL&lt;/span> TABLES &lt;span style="color:#66d9ef">IN&lt;/span> &lt;span style="color:#66d9ef">SCHEMA&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> backups;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">GRANT&lt;/span> &lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#66d9ef">ON&lt;/span> &lt;span style="color:#66d9ef">ALL&lt;/span> SEQUENCES &lt;span style="color:#66d9ef">IN&lt;/span> &lt;span style="color:#66d9ef">SCHEMA&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">to&lt;/span> backups;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">ALTER&lt;/span> &lt;span style="color:#66d9ef">DEFAULT&lt;/span> &lt;span style="color:#66d9ef">PRIVILEGES&lt;/span> &lt;span style="color:#66d9ef">IN&lt;/span> &lt;span style="color:#66d9ef">SCHEMA&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">GRANT&lt;/span> &lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#66d9ef">ON&lt;/span> TABLES &lt;span style="color:#66d9ef">TO&lt;/span> backups;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">ALTER&lt;/span> &lt;span style="color:#66d9ef">DEFAULT&lt;/span> &lt;span style="color:#66d9ef">PRIVILEGES&lt;/span> &lt;span style="color:#66d9ef">IN&lt;/span> &lt;span style="color:#66d9ef">SCHEMA&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">GRANT&lt;/span> &lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#66d9ef">ON&lt;/span> SEQUENCES &lt;span style="color:#66d9ef">TO&lt;/span> backups;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It&amp;rsquo;d be nice to find a way for the &lt;code>backups&lt;/code> user to automatically get read permissions, but I&amp;rsquo;ve yet to figure that out. I understood that the above &lt;code>ALTER DEFAULT PRIVILEGES&lt;/code> would cover it, but I seem to need to run it myself each time there is a change.&lt;/p></description></item><item><title>Managing Kubernetes certificates with Python</title><link>https://golder.org/posts/managing-kubernetes-certificates-with-python/</link><pubDate>Wed, 15 May 2019 00:00:00 +0000</pubDate><guid>https://golder.org/posts/managing-kubernetes-certificates-with-python/</guid><description>&lt;p>I run into a small stumbling block the other evening while working on my &amp;lsquo;site domain manager&amp;rsquo; project (for want of a better name). This is essentially a REST API running in a daemon service that manages the mappings of domains to websites, and uses &amp;lsquo;agents&amp;rsquo; to automate the configuration via API calls to the various services involved (domain registrars, DNS servers, WAF providers, SSL certs etc.)&lt;/p>
&lt;p>The problem arose because the manager class I was writing to interact with kubernetes needed to manage certificates. The &lt;a href="https://github.com/kubernetes-client/python">kubernetes python client library&lt;/a> has a whole bunch of useful higher-level API and model classes for listing, creating, updating the main models I needed to manage, such as the ConfigMap, DaemonSet, Service and Ingress, but because the Certificate is part of the cert-manager package, it doesn&amp;rsquo;t have the equivalent higher-level methods I needed.&lt;/p></description></item><item><title>Testing SMTP creds with Docker</title><link>https://golder.org/posts/testing-smtp-creds-with-docker/</link><pubDate>Sun, 24 Mar 2019 00:00:00 +0000</pubDate><guid>https://golder.org/posts/testing-smtp-creds-with-docker/</guid><description>&lt;p>One of our sites stopped sending it&amp;rsquo;s mail a few days ago. Unfortunately, the SMTP plugin used does not provide any debug logs of the SMTP connection, and it&amp;rsquo;s &amp;rsquo;test&amp;rsquo; tool just says that it sent the mail successfully. The logs for the SMTP service provider suggest they haven&amp;rsquo;t seen the connection. I issue a new password and reconfigure, still the same symptoms.&lt;/p>
&lt;p>So, I just want to do a simple check of the new SMTP creds. I also wanted to use a simple tool to avoid having to recall (Google) the magic SMTP protocol incantations and perform them via &amp;rsquo;telnet&amp;rsquo; or whatever. The &amp;lsquo;ssmtp&amp;rsquo; tool sprung to mind for some reason, so I figured I&amp;rsquo;d see how simple that would be to use in this case.&lt;/p></description></item><item><title>Monitoring Windows processes from Nagios</title><link>https://golder.org/posts/monitoring-windows-processes-from-nagios/</link><pubDate>Tue, 12 Mar 2019 00:00:00 +0000</pubDate><guid>https://golder.org/posts/monitoring-windows-processes-from-nagios/</guid><description>&lt;p>So, something I had to do recently was to set up monitoring for a couple of specific Windows processes, so that we get notification via a Discord channel if those processes are not running on various hosts.&lt;/p>
&lt;p>Typically, you&amp;rsquo;d do this with something like &lt;a href="https://www.nsclient.org/">NSClient++&lt;/a> but this was proving to be too problematic and time-consuming to get the client side configured and working correctly.&lt;/p>
&lt;p>So, I decided to try a different approach to keep things quick and simple. However, things turned out to be neither quick nor simple. I&amp;rsquo;ll save the rant, needless to say I generally leave managing our Windows hosts to our Windows people. I haven&amp;rsquo;t used Windows since &amp;lsquo;98. I typically steer clear, and I&amp;rsquo;m not even sure why I took this on, or why I&amp;rsquo;m writing this up. I hope it helps someone else, but I personally hope never to have to come back here!&lt;/p></description></item><item><title>Nginx Ingress access logs to ElasticSearch via syslog</title><link>https://golder.org/posts/nginx-ingress-syslogging-to-elasticsearch/</link><pubDate>Mon, 25 Feb 2019 00:00:00 +0000</pubDate><guid>https://golder.org/posts/nginx-ingress-syslogging-to-elasticsearch/</guid><description>&lt;p>There are several ways to extract the logs from a Kubernetes Nginx Ingress deployment into an ElasticSearch instance. One way I found was to use &lt;a href="https://www.elastic.co/products/beats/filebeat">Elastic Filebeats&lt;/a>, but I couldn&amp;rsquo;t find any really good examples of how to apply that to our cluster, and I felt it would clutter up the proxy servers with more containers they may not necessarily need.&lt;/p>
&lt;p>Instead, I chose to use &lt;a href="http://nginx.org/en/docs/syslog.html">nginx&amp;rsquo;s syslog&lt;/a> facility, which is a little more lightweight, and serves our purposes for now.&lt;/p></description></item><item><title>Simple LDAP proxy container</title><link>https://golder.org/posts/simple-ldap-proxy-container/</link><pubDate>Tue, 10 Apr 2018 00:00:00 +0000</pubDate><guid>https://golder.org/posts/simple-ldap-proxy-container/</guid><description>&lt;p>So, you have an LDAP server running happily on port 636 but one of your client applications doesn&amp;rsquo;t seem to be happy with the SSL connection for whatever reason. You need an intermediary container to handle the SSL connection to the LDAP server on port 636, presenting it to the local application on port 389.&lt;/p>
&lt;p>First, we write a Dockerfile that will describe a container that runs up an haproxy daemon.&lt;/p></description></item><item><title>Merging Confluence users</title><link>https://golder.org/posts/merging-confluence-users/</link><pubDate>Tue, 30 Sep 2014 00:00:00 +0000</pubDate><guid>https://golder.org/posts/merging-confluence-users/</guid><description>&lt;p>One of my clients is running Confluence. Somewhere along the line, two user accounts had been created for one user, and content had been added using both users.&lt;/p>
&lt;p>So muggins here to the rescue. Unfortunately, not much help to be found Googling, so I roll up my sleeves and dig into the Confluence DB schema. Oh what fun.&lt;/p>
&lt;p>The main users table appears to be the &amp;lsquo;user_mapping&amp;rsquo; table, where each user has a record. The user appears to have a long hash-like ID that represents them in any other records. I chose the ID of the account I would be merging from, and attempted to find the other tables involved in the database that would link to the record I am about to delete.&lt;/p></description></item><item><title>Building a Resilient Rural Network Infrastructure</title><link>https://golder.org/posts/2014-08-15-building-resilient-rural-network/</link><pubDate>Fri, 15 Aug 2014 00:00:00 +0000</pubDate><guid>https://golder.org/posts/2014-08-15-building-resilient-rural-network/</guid><description>Designing and implementing a robust dual-WAN network with wireless mesh connectivity to serve multiple households in rural Thailand, featuring automatic failover and centralized monitoring.</description></item><item><title>GStreamer pipeline for RTSP stream</title><link>https://golder.org/posts/gstreamer-pipeline-for-rtsp-stream/</link><pubDate>Tue, 21 May 2013 00:00:00 +0000</pubDate><guid>https://golder.org/posts/gstreamer-pipeline-for-rtsp-stream/</guid><description>&lt;p>A simple gstreamer pipeline to display at RTSP stream (from an Aircam)&amp;hellip;&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gst-launch -m rtspsrc location&lt;span style="color:#f92672">=&lt;/span>rtsp://172.16.2.251/live/ch00_0 ! rtph264depay ! ffdec_h264 ! ffmpegcolorspace ! autovideosink &lt;span style="color:#f92672">[&lt;/span>/code&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Harvest e-mail addresses from stdin</title><link>https://golder.org/posts/harvest-e-mail-addresses-from-stdin/</link><pubDate>Sat, 18 May 2013 00:00:00 +0000</pubDate><guid>https://golder.org/posts/harvest-e-mail-addresses-from-stdin/</guid><description>&lt;p>A little python script to do this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#!/usr/bin/env python&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> sys &lt;span style="color:#f92672">import&lt;/span> re
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>bulkemails &lt;span style="color:#f92672">=&lt;/span> sys&lt;span style="color:#f92672">.&lt;/span>stdin&lt;span style="color:#f92672">.&lt;/span>read()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># regex = whoEver@wHerever.xxx r = re.compile(&amp;#34;\[-a-zA-Z0-9.\_\]+@\[-a-zA-Z0-9\_\]+.\[a-zA-Z0-9\_.\]+&amp;#34;) results = r.findall(bulkemails)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>emails &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">for&lt;/span> x &lt;span style="color:#f92672">in&lt;/span> results: print str(x)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Magento API problem setting additional_attributes</title><link>https://golder.org/posts/harvest-e-mail-addresses-from-stdin/</link><pubDate>Sat, 18 May 2013 00:00:00 +0000</pubDate><guid>https://golder.org/posts/harvest-e-mail-addresses-from-stdin/</guid><description>&lt;p>For some reason, it&amp;rsquo;s damn hard to get the &lt;code>additional_attributes&lt;/code> set via Magento&amp;rsquo;s v2 API. Even the example code in their API docs doesn&amp;rsquo;t cover it. After trying many permutations, I finally managed to get it working with the following snippet of code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-php" data-lang="php">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">&amp;lt;?&lt;/span>&lt;span style="color:#a6e22e">php&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$soapopts &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">array&lt;/span>(&lt;span style="color:#e6db74">&amp;#39;trace&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;exceptions&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;features&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#a6e22e">SOAP_SINGLE_ELEMENT_ARRAYS&lt;/span>); $client &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> &lt;span style="color:#a6e22e">SoapClient&lt;/span> ( &lt;span style="color:#e6db74">&amp;#39;http://www.yourmagentosite.com/api/v2_soap/?wsdl&amp;#39;&lt;/span>, $soapopts); $session &lt;span style="color:#f92672">=&lt;/span> $client&lt;span style="color:#f92672">-&amp;gt;&lt;/span>&lt;span style="color:#a6e22e">login&lt;/span> ( &lt;span style="color:#e6db74">&amp;#39;apiuser&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;apipassword&amp;#39;&lt;/span> );
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$productData &lt;span style="color:#f92672">=&lt;/span> (&lt;span style="color:#a6e22e">object&lt;/span>)&lt;span style="color:#66d9ef">array&lt;/span>( &lt;span style="color:#e6db74">&amp;#39;additional_attributes&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> (&lt;span style="color:#a6e22e">object&lt;/span>)&lt;span style="color:#66d9ef">array&lt;/span>( &lt;span style="color:#e6db74">&amp;#39;single_data&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#66d9ef">array&lt;/span>( (&lt;span style="color:#a6e22e">object&lt;/span>)&lt;span style="color:#66d9ef">array&lt;/span>( &lt;span style="color:#e6db74">&amp;#39;key&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;custom_image_url&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;value&amp;#39;&lt;/span> &lt;span style="color:#f92672">=&amp;gt;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;http://www.yourmagentosite.com/nicepic.jpg&amp;#39;&lt;/span>, ), ), ), ); $result &lt;span style="color:#f92672">=&lt;/span> $client&lt;span style="color:#f92672">-&amp;gt;&lt;/span>&lt;span style="color:#a6e22e">catalogProductUpdate&lt;/span>($session, &lt;span style="color:#e6db74">&amp;#39;abjb91&amp;#39;&lt;/span>, $productData); &lt;span style="color:#66d9ef">print&lt;/span> $client&lt;span style="color:#f92672">-&amp;gt;&lt;/span>&lt;span style="color:#a6e22e">__getLastRequest&lt;/span>(); &lt;span style="color:#a6e22e">var_dump&lt;/span>($result);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item></channel></rss>