Using a docker proxy in WSL

I recently started working on a web-based project that has some non-standard networking stuff going on in the dev environment.  The fix was easy, but took a little searching, hence this post.

The codebase itself is built on Node JS, but I have to run it in WSL.  There are various build issues that keep it from building cleanly in Windows and it would be a bit of work to fix that.  But it builds just fine under WSL, so it's easier to use that.

The dev setup uses a Docker container running HAProxy.  It's set up to proxy the static portion of the website to the shared dev server (which is on the VPN) and direct the traffic for the dynamic part to a local port.  So we have a HAProxy running in Docker, the local dev server running in WSL, and the browser running in Windows.

The problem: In the default WSL config, this doesn't work.  The proxy to the shared server works just fine.  So if I go to wwwlocal.site.com, that works as a proxy to wwwdev.site.com.  Perfect!  However, wwwlocal.site.com/login is supposed to proxy to localhost:4321, but that results in a 503 error.  However, I can go directly to localhost:4321 and it works just fine, so this is clearly a networking issue.

The solution was to just add a few lines to my $home\.wslconfig file on Windows.  The following lines did the trick:

[wsl2]
networkingMode=mirrored
dnsTunneling=true
autoProxy=true

You can read more about these settings here.

Christmas mission 2023

Apparently this is just going to be a thing now.

This past Christmas marked the third annual Christmas Mission for my son.  It started simple enough, with a semi-educational crypto puzzle two years ago.  Then there was last year's mission, which involved multiple puzzles and a theme.  And he loved both of them, which is great.  But now it looks like I'm going to be coming up with one every year for the foreseeable future.

This year's mission was very involved.  Maybe a little too involved.  The Word document I used to take notes and print out the clues was eleven pages this time, including charts and graphics.  It had five "quests", plus the introduction and conclusion notes.  It took a while to complete, but apparently this one was a hit too, so I guess I'm doing something right.

This year, Zane wanted to be included in designing his mission.  Obviously the puzzles were up to me, but he had some requests for the theme.  Last year was Peanuts/World War I.  This year, he wanted it to be based on Minecraft.  And not just "regular Minecraft".  He came up with an elaborate plot outline involving an actualization of the Internet drama war between Minecraft and Fortnite, because apparently that's a thing.  It involved a spy mission, crafting things, resurrecting the Ender Dragon, and getting messages from various Mojang staffers.  I jotted all this down in OneNote and Zane double-checked my notes several times leading up to Christmas.  So at least he was engaged, which is good.

My first task was to try to hammer these notes into a semi-coherent story.  Since it was supposed to be a spy mission that involved an "actual war" between Minecraft and Fortnite and the resurrection of the Ender Dragon, I somehow got the rather dark idea of a mission to use the dragon as a biological weapon to destroy the world of Fortnite.  Granted, that probably constitutes genocide and is certainly a war crime, so not exactly a happy Christmas story.  But it made sense as a motivation and used the desired plot elements, so I ran with it.

The second task was to break this up into a series of missions.  One of Zane's notes involved creating an End Portal using the Eye of Ender.  Apparently you can craft this from some glass, a Ghast tear, and an Eye of Ender.  So I decided to base the missions around that.  We would gather the ingredients for an Eye of Ender so that he could craft the End Portal and send the Ender Dragon to the world of Fortnite.

But how do you resurrect an Ender Dragon?  I don't play Minecraft, so I've got no freakin' clue.  However, my sister-in-law was giving Zane a Dungeons and Dragons themed gift, which gave me an idea - magic!  Why not?  I mean, how would you resurrect a dragon?  When in doubt, just call it magic and get on with your life.

So with that, I came up with five quests.  For each one, I wrote a note from one of the aforementioned Minecraft staffers giving a puzzle to solve with a hint on the location of the next letter.  It started with an introduction letter laying out the quest to wipe out the world of Fortnite and directing him to the living room to find the first quest letter.  For the first three quests, I also included a printout of the ingredient itself, lifted from the Minecraft wiki.  The quests were as follows:

  1. First quest: Get the glass.  This involved solving a simple logic puzzle.  The glass was in one of the kitchen cabinets, but he couldn't just look in all of them because it's very delicate and too many vibrations would shatter it.  So the puzzle laid out a few rules and Zane had to deduce which cabinet it was in.  
  2. Second quest: Get the Ghast tear.  This one was a simple Morse code message.  The letter included a hint directing Zane to the bathroom to find a key to decode the Morse hidden with the spare toilet paper.  The decoded message directed him back to the living room to look under the coffee table.
  3. Third quest: Get the Eye of Ender.  This one was another relatively simple decoding, this time of an ASCII-encoded message.  This one was a two-step problem, as the letter contained a series of 8-bit binary numbers and a hint pointing to the kitchen silverware drawer, which contained an ASCII chart.  So Zane had to convert the binary to decimal and then do the character lookup.  The decoded message pointed to the bathroom sink, where the next message was hidden in the cabinet underneath.
  4. Fourth quest: Build the End portal.  This quest was a bit more involved.  The puzzle was to decode some text, but there was a different key for each line.  (There was only one word per line, so I was nice about that.)  A hint pointed Zane to a "decoder disk" that he got at a gift shop on our trip to Gettysburg last summer.  It came with directions on how to use a two-character key to decode a message.  So the letter said that each line had a different key at the start, followed by a space and then the message.  It actually took a bit to get Zane to figure out how that was supposed to work, but he did get it eventually.  The message led him to the coat closet, which had a Minecraft storage box that was supposed to serve as the crafting table to make the portal.
  5. Fifth quest: Resurrect the Ender Dragon.  This one was difficult.  To resurrect the Ender Dragon, the letter directed Zane to see his aunt, who was holding a "magic scroll" for him which would serve as the key.  The "scroll" was actually a printout of a page from an old Dungeons and Dragons manual, particularly the page for the "Wish" spell, which seemed an appropriate way to resurrect a dragon.  The puzzle was actually a set of 4--tuples, one per line, values separated by dashes.  At the bottom was a hint that these were coordinates, hinting that the page was divided into columns, which had lines, which had words, which had letters.  The idea was that, e.g. 2-5-3-4 would map to the fourth letter of the third word of the fifth line of the second column.  Clearly this was not as clear to Zane as it was in my head, because he had a hard time trying to figure out what he was supposed to do.  I eventually had to guide him through it, but once he got the idea he was able to decode the message successfully.  

The last message pointed Zane to his laptop.  His success letter was tucked inside, telling him that I'd installed a copies of Minecraft Java Edition and Minecraft Education Edition, as well as setting up a Minecraft server for him to play with.  He's been into Minecraft for a while, but hadn't had it on his computer, so that seemed like a suitable way to finish out a Minecraft-themed quest.

At the end of the day, Zane seemed pretty happy.  This was definitely a more involved mission than last year, and the puzzles were more challenging for him, but he said he enjoyed it.  So I count that as a success.  And at this rate, I suspect he'll want another mission this year, so I'll have to start planning earlier.

SSH Agent with Powershell

Another "note to my future self", recoded here for posterity.

Today I finally got around to making SSH agent work in Powershell with Git.  For the last year or so, I haven't had to deal with because my work mostly involved writing PHP code inside of WSL.  In that scenario, you're essentially using Linux, so ssh-agent works just fine.

But on native Windows with Powershell...not so much.

I mean, sure, I could just use Git BASH.  But why would I want to do that?  Powershell is awesome, and even if I probably know BASH better, I prefer Powershell.

But it turns out it wasn't all that difficult to get things working in Powershell.  There were two pieces to it:

  1. Use the right SSH agent (i.e. the one that comes with Git).
  2. Write a Powershell adapter for it.

It turns out that there's a Windows service for "OpenSSH Authentication Agent".  I'm not entirely sure what installs that, but apparently it's different from the ssh-agent that's installed with the Windows Git package and the ssh-add command from that doesn't seem to talk to it properly.

My solution was to just disable that service and use the plain-old ssh-agent that comes with Git.  The only problem with that is that the traditional invocation of eval `ssh-agent` doesn't work in Powershell because it outputs a BASH-formatted script.  But that's easily fixed with a couple of regular expressions.  So I added this to my Powershell $profile:

Set-Alias ssh-agent "$env:ProgramFiles\git\usr\bin\ssh-agent.exe"
Set-Alias ssh-add "$env:ProgramFiles\git\usr\bin\ssh-add.exe"

# Need to turn off Open SSH Authentication Agent system service,
# then run the ssh-agent from Git.
function Start-SshAgent {
    $output = (ssh-agent)
    $sock_match = ($output | Select-String -Pattern 'SSH_AUTH_SOCK=(\S+);')
    $sock_path = $sock_match[0].Matches.Groups[1].Value
    $pid_match = ($output | Select-String -Pattern 'SSH_AGENT_PID=(\d+);')
    $agent_pid = $pid_match[0].Matches.Groups[1].Value
    $env:SSH_AUTH_SOCK = $sock_path
    $env:SSH_AGENT_PID = $agent_pid
    Write-Output "Agent pid $agent_pid"
}

Start-SshAgent

And there we go!  Now I can just run ssh-add to add my key and Git picks it up as expected.

Minor WSL GUI annoyance

Minor annoyance: For some reason, WSL GUI windows like to hide themselves.  I'm not sure why.

I see this all the time on my Windows 10 work laptop, where I use WSL extensively.  It usually happens after my laptop has gone to sleep.  I come back in the morning, log in, and all of my open WSL gVim windows are just gone.  The processes are still running, they're just not displayed and they're MIA from the Windows taskbar.

Fortunately, this is easily fixed by just starting a new WSL GUI app.  So I generally just pop up my Windows Terminal and start a new gVim instance, and then the running instances pop back into existence and they're fine until the system sleeps again.

I have no idea why that happens, nor if it's specific to Windows 10 or anything else.  It's just one of those things that's annoying but not a big deal.  Rant over.

Sometimes WSL needs an update, apparently

Note to self: Sometimes, it seems that WSL needs to be updated.  I mean, yes, obviously it does.  But apparently sometimes Windows updates will break it and you need to manually intervene.

I saw this the other day.  At some point, something got updated and when I tried to open a new WSL terminal, it would always fail with the message Error code: Wsl/Service/0x80040326.  Oddly enough, the WSL shells and processes I already had running still worked, I just couldn't start any new ones.

Luckily, the fix is pretty simple.  You just have to open up a Powershell instance run wsl --update to update WSL and then wsl --shutdown to reboot it.  That's it.  It's just annoying because that's a manual process - you still need to manually run the update, even if you reboot your computer.