Try reloading the player or open this episode directly on YouTube.
This episode of Ship It Weekly discusses AWS Gateway Load Balancer's new TCP Reset feature, enhancing recovery from network failures. It also covers Azure DevOps' migration tools to GitHub, GitHub's enforcement of self-hosted runners, and Docker security risks.
Now Playing
AWS GWLB TCP Reset, Azure DevOps Live Migrations to GitHub, GitHub Runner Enforcement, Docker Root Risk, Lambda IAM Updates, PostgreSQL Upgrade Traps, SonicWall Zero-Days & Better Incident Reviews
Ship It Weekly
0:0017:26
Chapters
Jump to a section in this episode.
Speed & share
Transcript
AWS can now reset failed Gateway Load Balancer
connections instead of letting them hang for
minutes. GitHub is getting ready to stop running
jobs on stale self-hosted Actions runners. And
a Linux desktop setup accidentally gave basically
every user-space process a path to root through
Docker. This week is mostly about failure modes
hiding inside things we already trust. I'm Brian
Teller from Teller's Tech, and this is Ship It
Weekly. Welcome back to Ship It Weekly, the show
about the DevOps, SRE, cloud, platform, and security
stories that matter when you are the person keeping
the thing running at three in the morning. For
the weekly story list and source links, check
out OnCallBrief.com. For past episodes and show
notes, head over to ShipItWeekly.fm. This week,
AWS Gateway Load Balancer gets TCP Reset to
shorten recovery when a firewall appliance dies.
Azure DevOps gets live migration tooling for
moving enterprises to GitHub. GitHub is starting
enforcement against stale self-hosted actions
runners. And Omarchy shipped with a Docker configuration
that effectively made normal desktop processes
root-capable. Then we have a quick lightning round
and a human closer about whether incident reviews
should happen asynchronously first, live later.
Let's get into it. First up, AWS Gateway Load
Balancer now supports TCP Reset. This is one
of those small-looking features that solves a
pretty ugly failure mode. Gateway Load Balancer
is commonly used to put firewalls, intrusion
detection systems, and other network appliances
inline with application traffic. When one of
those appliances fail, new connections can usually
move to a healthy target. Existing TCP connections
are harder. They may keep trying to use the failed
path until the connection times out. Depending
on the application and TCP settings, that can
mean 30 seconds. It can also mean several minutes.
From the application's point of view, the network
is not obviously broken. It is just hanging.
With TCP Reset enabled, Gateway Load Balancer
can actively terminate those affected connections.
The client gets a reset immediately and can reconnect
through a healthy appliance. That turns an ambiguous
failure into an explicit one. And I think that
is the useful lesson here. Fast failure is often
better than slow uncertainty. A failed request
gives the application something that it can react
to. A connection sitting there doing nothing
is much harder. Retries do not happen yet. Timeout
budgets get consumed. Users wait. Threads stay
blocked. And everything upstream looks degraded
instead of clearly failed. We talk a lot about
graceful failover, but sometimes graceful means
being very direct. This path is dead. Start over
somewhere healthy. This is especially important
around stateful middleboxes because the backend
application may be perfectly fine while the network
path in front of it is not. The shorter you can
make that ambiguity window, the faster the rest
of the system can recover. Next, Microsoft has
put Enterprise Live Migrations from Azure DevOps
to GitHub into public preview. This is aimed
at large migrations where shutting down engineering
for a weekend is not really an option. The idea
is that repositories remain writable in Azure
DevOps while changes continuously synchronize
into GitHub Enterprise Cloud. Teams keep working
during most of the migration. Then, when it is
time for the final cutover, you stop writes,
sync the remaining changes, and switch over.
Microsoft says the final downtime can typically
stay under about 30 minutes. The migration tooling
can also convert Azure DevOps branch policies
into GitHub rulesets. And importantly, Azure
pipelines can continue running while repositories
move. That matters because repository migration
is rarely just copying Git objects. The hard
part is everything attached to the repository.
Permissions, branch protections, CI/CD. webhooks,
secrets, bots, service connections, release workflows,
and all of the little assumptions around URLs
and identities that accumulate over 10 years.
A migration can be technically successful and
still be operationally terrible if engineers
lose half a day every time you move another batch
of repositories. Live synchronization changes
that equation. It lets the migration behave more
like a controlled transition instead of a hard
stop. But I would still treat this like any other
production migration. Inventory what depends
on Azure DevOps before you move anything. Test
policy conversions. Validate pipelines. Know
what happens to automation that references old
URLs. And have a rollback path for the cutover
itself. The goal is not just moving the code.
The goal is moving the development system around
the code without everybody discovering the missing
pieces on Monday morning. Third, GitHub is getting
serious about stale self-hosted Actions runners.
This one is worth checking if you manage your
own runner fleet. GitHub is rolling out minimum
version enforcement. Self -hosted runners need
to be at least version 2.329.0 to register.
And runners also need to stay within 30 days
of current releases to continue executing jobs.
GitHub is starting runtime brownouts on September
9th. Full enforcement is planned for September
25th. The obvious action is to update your runners.
The more interesting question is why they are
stale in the first place. A lot of teams treat
self-hosted runners like infrastructure appliances.
Build the image. Deploy the runner. Forget about
it until something breaks. But the runner is
executing arbitrary CI workloads with access
to repositories, credentials, networks, artifact
stores, and often cloud permissions. It is part
of your security boundary. It is also a client
of GitHub's service. And that client has to evolve
with the platform. If your upgrade process requires
somebody remembering to rebuild an AMI every
few months, enforcement like this is eventually
going to catch you. This is where ephemeral runners
are really attractive. Start from a maintained
image. Run the job. Destroy the environment.
Regularly rebuild the base image and roll forward
automatically. That does not eliminate maintenance.
It makes maintenance part of the lifecycle instead
of an exception to it. And if you cannot automatically
replace a runner fleet today, this brownout is
probably a good excuse to figure out why. Because
September 25th is a much nicer time to discover
that problem than during a production deployment.
Fourth, Omarchy fixed a configuration that effectively
gave user-space applications a path to root through
Docker. The default user was part of the Docker
group. That is common on Linux systems because
it lets you run Docker commands without sudo.
It is also basically root access. The Docker
daemon runs as root. If your user can talk to
the Docker socket, you can generally start a
privileged container, mount the host filesystem,
and modify the machine. So anything running as
that user inherits a very powerful capability,
a browser extension, an IDE plugin, a compromised
development tool, a coding agent, malware running
in the session. If it can reach the Docker socket,
the jump from normal user to root may be trivial.
Omarchy fixed this in version 4.0.1. But the
bigger point is not really about Omarchy. The
Docker group has been a known privilege boundary
for a long time. What has changed is how much
software we now run inside developer sessions.
IDE extensions, package managers, AI coding tools,
local agents, browser automation, dev containers,
CLI plugins. A modern workstation is a pretty
busy environment. Giving every one of those processes
indirect root access because Docker is more convenient
without sudo is worth thinking about. Convenience
is fine. Just name the privilege correctly. If
membership in a group means the user can become
root, treat that group like root access. Because
an attacker absolutely will. Quick lightning
round. First, AWS Lambda now supports fuller
IAM resource-based policies. You can define
multiple principals and actions and use normal
IAM condition keys for cross-account access.
That should make larger Lambda authorization
models cleaner than some of the older one permission
at a time workflows. Second, AWS is warning about
PostgreSQL upgrades that can fail because of
circular role memberships. RDS and Aurora upgrades
from PostgreSQL 14 or earlier to 15 and newer
can hit problems if roles like rds_
superuser and the built-in read-all or write-
all roles end up referencing each other. AWS
has a pre-upgrade query to find the problem.
Not exciting. Very useful before your maintenance
window. Third, a researcher released a proof
of concept called FalconFlank, claiming a CrowdStrike
privilege escalation path. Right now, I would
treat this one carefully. There is public research
and a PoC, but no CVE, affected version matrix,
or CrowdStrike advisory yet. Interesting enough
to watch though. Not enough evidence to panic.
And fourth, SonicWall SMA1000 appliances have
zero-day vulnerabilities being actively exploited.
If those appliances are in your environment,
this is firmly in the patch-now category. Internet-
facing remote access infrastructure is not where
I want to wait and see how exploitation develops.
The human closer this week is about incident
reviews, specifically whether distributed teams
should start them asynchronously instead of immediately
scheduling another meeting. One SRE team wrote
about reconstructing a 37-minute outage almost
entirely async. People added logs, timelines,
screenshots, metrics, code references, and corrections
directly into the review document. That part
worked really well. It gave people time to check
evidence instead of relying on memory in a meeting.
Someone could make a claim, and another engineer
could verify it. The timeline became much more
precise, and people in different time zones could
contribute without everybody needing to be online
at once. But there was a downside. Disagreements
became long comment threads. Questions about
causality got buried. And action-item ownership
became fuzzy. That sounds pretty familiar. Async
communication is really good at collecting information.
It is not always good at resolving ambiguity.
A document can tell you what happened. A comment
thread can show that three people disagree about
why it happened. At some point, putting those
three people in the same conversation is probably
faster. The model they landed on was basically
async first, live later. Use the document to
gather evidence and build the timeline. Then
hold a short live review only for the things
that actually need discussion. Disputed causes,
tradeoffs, decisions, and ownership. I like
that a lot because the goal of a postmortem
is not having a postmortem meeting. The goal
is understanding the incident well enough that
the organization learns something from it. Sometimes
meetings help with that, but sometimes they just
force 12 people to watch one person scroll through
Datadog. Async evidence gathering also changes
who gets heard. In a live review, the people
who remember the incident fastest or speak the
most confidently can shape the narrative early.
In a shared document, somebody can go back, check
the logs and say, actually, that happened four
minutes later. Or this alert fired before the
deployment. Or the database was already degraded
before the API started failing. That is useful.
But the async part still needs structure. Someone
owns the timeline. Someone identifies unresolved
questions. Someone turns the discussion into
actions. And someone actually owns those actions
afterward. Otherwise, you just traded a bad meeting
for a very detailed document nobody closes. The
thing I like most here is that it treats communication
style as part of incident response design. Distributed
teams do not have to recreate the same meeting-
heavy process companies used when everybody
sat in one office. Use async for the things async
does well. Use live conversation for the things
humans resolve better together. And keep both
focused on learning, not blame. Because the best
incident review is not the one with the most
complete document. It is the one that actually
changes how the next incident goes. That's it
for this week's Ship It Weekly. We covered Gateway
Load Balancer TCP Reset, Azure DevOps Live Migrations
to GitHub, GitHub's self-hosted Actions runner
enforcement, and the Omarchy Docker privilege
issue, plus Lambda IAM policy improvements, Postgres
SQL upgrade traps, the FalconFlank research,
and SonicWall zero-days. Follow or subscribe
wherever you are watching or listening. You can
find the weekly story list and source links at
OnCallBrief.com. and past episodes and show
notes at ShipItWeekly.fm. I'm Brian Teller from
Teller's Tech. Thanks for listening. And remember,
The thing that stood out to me most this week is how many infrastructure problems are not really caused by complete failure. They are caused by systems failing slowly, quietly, or in ways that look healthy from the outside.
The Gateway Load Balancer story is probably the clearest example. If an inline firewall or network appliance dies, the backend application might still be perfectly healthy. The problem is the path between the client and that application. Existing TCP connections can sit there for minutes waiting to time out, which means the system is technically broken but nothing has failed clearly enough for the rest of the stack to react.
That is why I like the TCP Reset feature.
Sometimes the best recovery mechanism is just telling the truth faster.
This connection is dead. Stop waiting. Try again somewhere else.
We spend a lot of time designing systems to avoid failures, but failures are inevitable. What matters just as much is how obvious the failure is when it happens. A clean error can trigger retries, failover, alerts, or fallback behavior. A request that hangs for three minutes just consumes time, connections, threads, and patience.
Fast failure sounds harsh, but operationally it can be much kinder.
The Azure DevOps migration story is another version of that same idea, just applied to change instead of failure.
Moving repositories sounds simple until you have actually been involved in a large migration. The Git objects are usually the easy part. Everything surrounding them is where the real work lives.
Branch protections.
Pipelines.
Webhooks.
Service accounts.
Secrets.
Release automation.
Permissions.
Bots.
Scripts with hardcoded repository URLs that somebody wrote six years ago and nobody remembers.
The fact that Microsoft is building live synchronization into the migration process is important because it recognizes that engineering cannot just stop while the platform team moves everything around.
A good migration should feel boring to the people using the system.
That usually means doing more work behind the scenes so everybody else can keep working.
But live migration does not remove risk. It changes where the risk is.
Instead of one giant cutover, you now have a period where two systems exist at the same time. That means you need to understand synchronization, identity, policy conversion, and exactly what happens during the final handoff.
It is still a production migration.
The tooling just gives you a better way to control the blast radius.
The GitHub runner story is probably the most immediately actionable one this week.
Self-hosted runners are really easy to forget about.
They run.
Jobs pass.
Nobody touches them.
Eventually they become part of the furniture.
But those runners are executing code with access to repositories, credentials, internal networks, artifact stores, cloud accounts, and deployment systems.
They are not just build machines.
They are part of your security boundary.
So GitHub enforcing minimum versions makes sense.
And if that enforcement breaks your pipeline, the real problem probably is not GitHub enforcing an update.
The problem is that your runner maintenance process depended on nobody noticing they were old.
That is where ephemeral infrastructure helps.
If runners are created from a maintained image, execute a job, and disappear, then replacing them becomes normal instead of disruptive.
You still have to maintain the image.
You still need patching.
You still need testing.
But the lifecycle becomes something you expect instead of something you postpone.
That is a pattern I think applies to a lot more than CI runners.
Infrastructure that is easy to replace tends to be infrastructure that is easier to maintain.
The Docker story is the one that I think developers should pay the most attention to.
Adding yourself to the Docker group has been normal advice for years.
Nobody wants to type sudo every time they run a container.
The problem is that Docker is not just another CLI.
If your user can control a root-owned Docker daemon, your user can effectively become root.
That has always been true.
What has changed is how crowded developer machines have become.
A modern development workstation might have browser extensions, IDE plugins, package managers, coding agents, local automation, dev containers, AI tools, shell plugins, and who knows what else running in the same user session.
Every additional process expands the amount of software you are trusting.
So when we say, "the developer has access to Docker," what we may really be saying is, "anything running as the developer has access to something that can become root."
Those are not quite the same statement.
And with more autonomous tooling running locally, I think that distinction matters more now than it did a few years ago.
This is not really an argument against Docker.
It is an argument for being accurate about the privilege you are granting.
Convenience is fine.
Just do not call something low privilege when it is not.
Even the smaller stories this week fit into the same theme.
Lambda getting fuller resource-based IAM policies is mostly about giving operators better ways to express access intentionally.
The PostgreSQL upgrade issue is a reminder that configuration relationships that work perfectly for years can suddenly matter during a version boundary.
The CrowdStrike research is a good example of why security reporting needs some patience. A public PoC is interesting, but interesting is not the same thing as confirmed impact across every environment.
And SonicWall is the opposite situation. Active exploitation against Internet-facing remote access infrastructure is exactly the kind of thing where waiting for more discussion is probably not the right move.
Knowing the difference between "watch this" and "patch this now" is part of the job.
The incident review story probably ties everything together best.
I like the idea of async-first incident reviews because the first job after an incident should be figuring out what actually happened.
Not what everybody remembers happening.
Not what the loudest person in the meeting thinks happened.
What the evidence says happened.
Logs.
Metrics.
Deployments.
Alerts.
Code changes.
Timelines.
Async work gives people time to go find those things.
It also lets somebody come back and say, "Actually, that event happened four minutes later," without interrupting somebody halfway through a conference-room explanation.
But async communication has limits too.
You can collect facts asynchronously.
It is harder to resolve disagreement asynchronously.
A twenty-comment thread arguing about whether a database issue caused an API failure or the API failure overloaded the database is probably a sign that three people need to talk for fifteen minutes.
That is why I like the async-first, live-later approach.
Do not schedule twelve people for an hour just to reconstruct a timeline that could have been built in a document.
Build the timeline first.
Gather the evidence.
Identify what is actually disputed.
Then get the right people together and resolve those specific questions.
And most importantly, leave with owners.
Because a beautifully written incident review with no one responsible for the follow-up work is just documentation.
The common thread across all of these stories is that reliable systems tend to make reality explicit.
A dead connection should look dead.
A migration should have a defined cutover.
A runner should have a lifecycle.
A privileged user should be treated as privileged.
A security claim should be separated from a confirmed vulnerability.
And an incident review should separate what the evidence shows from what people assume happened.
A lot of operational pain comes from ambiguity.
The system is sort of working.
The migration is mostly done.
The runner is probably current.
The Docker group is basically harmless.
We think this caused the outage.
Those are comfortable statements right up until they are not.
Good engineering is often just removing enough ambiguity that when something changes, the system and the people operating it can react quickly.
📝 Notes
Show Notes
This week on Ship It Weekly: AWS Gateway Load Balancer gets TCP Reset, giving applications a faster way to recover when firewalls or other inline appliances fail instead of waiting minutes for TCP retries to time out. Microsoft puts Enterprise Live Migrations into public preview for moving Azure DevOps repositories to GitHub Enterprise Cloud with data residency while developers keep working. GitHub is beginning enforcement against outdated self-hosted Actions runners. And Omarchy fixes a Docker configuration that effectively gave normal desktop processes a path to root.
The bigger theme this week is failure modes hiding inside infrastructure we already trust. A dead network path can look like a slow application. A repository migration involves far more than copying Git history. A self-hosted runner can quietly become unsupported while it continues looking healthy. And giving a developer access to the Docker socket may sound like convenience until you remember that the Docker group is effectively a root-level privilege.
In the lightning round: Lambda gets full IAM resource-based policies, AWS warns that circular PostgreSQL role memberships can stall major RDS and Aurora upgrades, a researcher releases the FalconFlank CrowdStrike privilege-escalation PoC while CrowdStrike investigates, and SonicWall patches two SMA1000 zero-days after confirming active exploitation.
The thing that stood out to me most this week is how many infrastructure problems are not really caused by complete failure. They are caused by systems failing slowly, quietly, or in ways that look healthy from the outside.
The Gateway Load Balancer story is probably the clearest example. If an inline firewall or network appliance dies, the backend application might still be perfectly healthy. The problem is the path between the client and that application. Existing TCP connections can sit there for minutes waiting to time out, which means the system is technically broken but nothing has failed clearly enough for the rest of the stack to react.
That is why I like the TCP Reset feature.
Sometimes the best recovery mechanism is just telling the truth faster.
This connection is dead. Stop waiting. Try again somewhere else.
We spend a lot of time designing systems to avoid failures, but failures are inevitable. What matters just as much is how obvious the failure is when it happens. A clean error can trigger retries, failover, alerts, or fallback behavior. A request that hangs for three minutes just consumes time, connections, threads, and patience.
Fast failure sounds harsh, but operationally it can be much kinder.
The Azure DevOps migration story is another version of that same idea, just applied to change instead of failure.
Moving repositories sounds simple until you have actually been involved in a large migration. The Git objects are usually the easy part. Everything surrounding them is where the real work lives.
Branch protections.
Pipelines.
Webhooks.
Service accounts.
Secrets.
Release automation.
Permissions.
Bots.
Scripts with hardcoded repository URLs that somebody wrote six years ago and nobody remembers.
The fact that Microsoft is building live synchronization into the migration process is important because it recognizes that engineering cannot just stop while the platform team moves everything around.
A good migration should feel boring to the people using the system.
That usually means doing more work behind the scenes so everybody else can keep working.
But live migration does not remove risk. It changes where the risk is.
Instead of one giant cutover, you now have a period where two systems exist at the same time. That means you need to understand synchronization, identity, policy conversion, and exactly what happens during the final handoff.
It is still a production migration.
The tooling just gives you a better way to control the blast radius.
The GitHub runner story is probably the most immediately actionable one this week.
Self-hosted runners are really easy to forget about.
They run.
Jobs pass.
Nobody touches them.
Eventually they become part of the furniture.
But those runners are executing code with access to repositories, credentials, internal networks, artifact stores, cloud accounts, and deployment systems.
They are not just build machines.
They are part of your security boundary.
So GitHub enforcing minimum versions makes sense.
And if that enforcement breaks your pipeline, the real problem probably is not GitHub enforcing an update.
The problem is that your runner maintenance process depended on nobody noticing they were old.
That is where ephemeral infrastructure helps.
If runners are created from a maintained image, execute a job, and disappear, then replacing them becomes normal instead of disruptive.
You still have to maintain the image.
You still need patching.
You still need testing.
But the lifecycle becomes something you expect instead of something you postpone.
That is a pattern I think applies to a lot more than CI runners.
Infrastructure that is easy to replace tends to be infrastructure that is easier to maintain.
The Docker story is the one that I think developers should pay the most attention to.
Adding yourself to the Docker group has been normal advice for years.
Nobody wants to type sudo every time they run a container.
The problem is that Docker is not just another CLI.
If your user can control a root-owned Docker daemon, your user can effectively become root.
That has always been true.
What has changed is how crowded developer machines have become.
A modern development workstation might have browser extensions, IDE plugins, package managers, coding agents, local automation, dev containers, AI tools, shell plugins, and who knows what else running in the same user session.
Every additional process expands the amount of software you are trusting.
So when we say, "the developer has access to Docker," what we may really be saying is, "anything running as the developer has access to something that can become root."
Those are not quite the same statement.
And with more autonomous tooling running locally, I think that distinction matters more now than it did a few years ago.
This is not really an argument against Docker.
It is an argument for being accurate about the privilege you are granting.
Convenience is fine.
Just do not call something low privilege when it is not.
Even the smaller stories this week fit into the same theme.
Lambda getting fuller resource-based IAM policies is mostly about giving operators better ways to express access intentionally.
The PostgreSQL upgrade issue is a reminder that configuration relationships that work perfectly for years can suddenly matter during a version boundary.
The CrowdStrike research is a good example of why security reporting needs some patience. A public PoC is interesting, but interesting is not the same thing as confirmed impact across every environment.
And SonicWall is the opposite situation. Active exploitation against Internet-facing remote access infrastructure is exactly the kind of thing where waiting for more discussion is probably not the right move.
Knowing the difference between "watch this" and "patch this now" is part of the job.
The incident review story probably ties everything together best.
I like the idea of async-first incident reviews because the first job after an incident should be figuring out what actually happened.
Not what everybody remembers happening.
Not what the loudest person in the meeting thinks happened.
What the evidence says happened.
Logs.
Metrics.
Deployments.
Alerts.
Code changes.
Timelines.
Async work gives people time to go find those things.
It also lets somebody come back and say, "Actually, that event happened four minutes later," without interrupting somebody halfway through a conference-room explanation.
But async communication has limits too.
You can collect facts asynchronously.
It is harder to resolve disagreement asynchronously.
A twenty-comment thread arguing about whether a database issue caused an API failure or the API failure overloaded the database is probably a sign that three people need to talk for fifteen minutes.
That is why I like the async-first, live-later approach.
Do not schedule twelve people for an hour just to reconstruct a timeline that could have been built in a document.
Build the timeline first.
Gather the evidence.
Identify what is actually disputed.
Then get the right people together and resolve those specific questions.
And most importantly, leave with owners.
Because a beautifully written incident review with no one responsible for the follow-up work is just documentation.
The common thread across all of these stories is that reliable systems tend to make reality explicit.
A dead connection should look dead.
A migration should have a defined cutover.
A runner should have a lifecycle.
A privileged user should be treated as privileged.
A security claim should be separated from a confirmed vulnerability.
And an incident review should separate what the evidence shows from what people assume happened.
A lot of operational pain comes from ambiguity.
The system is sort of working.
The migration is mostly done.
The runner is probably current.
The Docker group is basically harmless.
We think this caused the outage.
Those are comfortable statements right up until they are not.
Good engineering is often just removing enough ambiguity that when something changes, the system and the people operating it can react quickly.