Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions content/hardening-k8s.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Cover: images/covers/hardening-k8s.png

[TOC]

Securing Kubernetes workloads isn't just about scanning images or tweaking RBAC, it's about enforcing the right guardrails at the pod level to minimize risk by default. This post shares field-tested strategies aligned with the Pod Security Standards (Restricted profile) to help you build safer, production-grade deployments.
Scanning images and tweaking RBAC only get you so far; securing Kubernetes workloads means enforcing the right guardrails at the pod level so risk is minimized by default. This post shares field-tested strategies aligned with the Pod Security Standards (Restricted profile) to help you build safer, production-grade deployments.

## Key Practices for Hardening Kubernetes Deployments

Expand Down Expand Up @@ -138,6 +138,6 @@ Disable automatic token mounting for pods that don't need API server access. Cre

## Final Thoughts

Security isn't just about tools, it's about secure defaults. These practices help harden your Kubernetes workloads using the Restricted Pod Security Standard and reduce risks across the board.
Tools matter less than secure defaults. These practices help harden your Kubernetes workloads using the Restricted Pod Security Standard and reduce risks across the board.

If you're managing production clusters or sensitive environments, these changes are low-hanging fruit with a high return on security posture. The same "safe by default" mindset applied to AWS IAM is the subject of [IAM Roles That Fail Loud]({filename}iam-safe-defaults-fail-loud.md); for the node side of memory pressure, see [Taming the OOM Killer]({filename}oom-killer-process-prioritization.md).
12 changes: 6 additions & 6 deletions content/iam-blast-radius-architecture-problem.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ Category: DevSecOps
Tags: aws, iam, devsecops, cloud-security, threat-modeling
Slug: iam-blast-radius-architecture-problem
Og_image: images/og/iam-blast-radius-architecture-problem.png
Summary: Most IAM reviews start too late, after the account structure and trust boundaries are set. Least privilege isn't fewer actions; it's smaller failure domains.
Summary: Most IAM reviews start too late, after the account structure and trust boundaries are set. Least privilege means smaller failure domains, not fewer actions.
Cover: images/covers/iam-blast-radius-architecture-problem.png

[TOC]

## Most IAM reviews start too late

The problem is not the policy itself. It is that the policy shows up last.
The policy itself is rarely the problem; its timing is. The policy shows up last.

By the time someone is staring at a JSON policy, many of the important security decisions have already been made. The account structure exists. The trust relationships exist. The CI/CD path exists. The Terraform state exists. The production data, deployment roles, and trust boundaries are already in place.

Expand All @@ -29,8 +29,8 @@ A lot of IAM programs stay shallow because they focus on permissions as isolated

Least privilege is not just fewer actions. Least privilege is smaller failure domains.

- A CI role that can deploy to every environment is not just a deployment role. It is a production-wide failure domain.
- A Terraform role that can modify IAM, networking, logging, and state stores is not just infrastructure automation. It is a control plane for the company.
- A CI role that can deploy to every environment is a production-wide failure domain, whatever its name says.
- A Terraform role that can modify IAM, networking, logging, and state stores is a control plane for the company that happens to be labeled infrastructure automation.
- A workload identity that can read secrets, write artifacts, or assume a runner role is not just an application permission. It may be a persistence path.
- A Kubernetes service account that can patch workloads, mount secrets, or exec into pods is not just a cluster detail. It can become a lateral movement primitive.

Expand Down Expand Up @@ -95,7 +95,7 @@ Containment is a design choice made before the policy review begins:
- **Separate deployment identities by app and environment.** An incident in staging or an isolated service should not be able to cascade across boundaries.
- **Keep identity mutation behind tighter controls.** Restrict rights to modify roles, policies, and trust relationships far more aggressively than ordinary infrastructure changes.
- **Protect Terraform state like sensitive production data.** Treat state files as high-risk assets containing secrets, architecture maps, and privileged paths.
- **Treat CI/CD as privileged infrastructure.** Build pipelines are not generic automation; they are execution engines with direct access to production state.
- **Treat CI/CD as privileged infrastructure.** Build pipelines are execution engines with direct access to production state, not generic automation.
- **Make role assumption paths explicit.** Require clear, auditable trust policies rather than broad, ambient role-chaining.
- **Detect sensitive actions, not just policy drift.** Monitor for abnormal role assumptions, key generation, and identity mutation in near-real time.
- **Make rollback work without the compromised identity.** Break-glass and incident containment paths must not depend on the very principals being isolated.
Expand All @@ -108,7 +108,7 @@ The safer default is easier to enforce when it is baked into the tooling. A smal

## Trust made visible

IAM is not only an access control system. It is one of the main ways cloud architecture expresses trust. Every role, policy, permission boundary, service account, and deployment credential says something about what the system believes can safely happen.
Beyond access control, IAM is one of the main ways cloud architecture expresses trust. Every role, policy, permission boundary, service account, and deployment credential says something about what the system believes can safely happen.

When those beliefs are wrong, the incident does not stay inside the JSON file. It leaks through the architecture.

Expand Down
2 changes: 1 addition & 1 deletion content/oom-killer-process-prioritization.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ sudo systemctl enable --now oom-adjuster.service

## Security Considerations

From a [DevSecOps perspective](devsecops-guide.html), OOM prioritization is not just about uptime. It's a security hardening technique:
From a [DevSecOps perspective](devsecops-guide.html), OOM prioritization is a security hardening technique as much as an uptime one:

- **SSHD protection** prevents lockouts during memory exhaustion.
- **Preserving portsentry or IDS processes** ensures defense mechanisms remain active.
Expand Down
2 changes: 1 addition & 1 deletion content/pages/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,6 @@ Work under [@oliveratprimer](https://github.com/oliveratprimer) includes:

---

> Security is not a feature. It is infrastructure.
> Security is infrastructure, not a feature.

All contributions are built for clarity, reproducibility, and operational reliability.
4 changes: 2 additions & 2 deletions content/paved-road-adoption-as-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Cover: images/covers/paved-road-adoption-as-control.png

## Adoption is a security control

A security control developers can route around is not a control. It is a checkbox.
A security control developers can route around is a checkbox, not a control.

In a previous life, I watched our cloud security model fail in a very specific way. Developers would hit a build break, file a ticket, and ship the workload through a side door. The controls all worked on paper. The audit report read fine. Adoption was the problem, and adoption is what determines whether a control exists in practice or only in slides.

Expand Down Expand Up @@ -53,7 +53,7 @@ Adoption stalled for **four months**.

What was happening in retrospect is obvious: as long as two paths existed, the new path was extra work for any team that already had a working pattern in the old one. The new path was easier for greenfield workloads, but most workloads were not greenfield, and the migration cost is what dominated.

I retired the legacy patterns hard. Stopped accepting them in CI, marked them deprecated in code review, and gave teams a fixed window to migrate. Adoption climbed within a single sprint. The lesson - one I keep relearning in different forms - is that *paths in addition* are not adopted, paths in *replacement* are. If you are not willing to break the side door, you are not actually building a paved road. You are building a recommendation.
I retired the legacy patterns hard. Stopped accepting them in CI, marked them deprecated in code review, and gave teams a fixed window to migrate. Adoption climbed within a single sprint. The lesson - one I keep relearning in different forms - is that *paths in addition* are not adopted, paths in *replacement* are. Unless you are willing to break the side door, what you are building is a recommendation, not a paved road.

## What I underestimated: the political cost

Expand Down
4 changes: 2 additions & 2 deletions content/pwnagotchi-plugin-bandit-hardening.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Bandit is `pip install bandit` and it ships with rules numbered B602, B603, B607
- **B603**: `subprocess` without an explicit shell, but the binary path comes from a variable rather than a literal. Bandit can't tell whether that variable is trusted.
- **B607**: starting a process with a partial executable path. `subprocess.run(["nmcli", "..."])` triggers this because `nmcli` is resolved through `$PATH`, and `$PATH` is something you should not trust on a device whose disk has been out of your sight.

Pyflakes, Ruff, mypy will not flag any of these. They are not type or syntax errors. They are policy.
Pyflakes, Ruff, mypy will not flag any of these, because these are policy findings, not type or syntax errors.

A clean Bandit run on a hardened plugin looks like this:

Expand Down Expand Up @@ -175,7 +175,7 @@ Pin the Bandit version. Bandit's rule set evolves; an unpinned `bandit` in CI me

## The pattern generalizes

The four moves (resolve binaries with `shutil.which()` at init, pass argv as lists, validate inputs at the trust boundary, annotate `# nosec` per-call with a structural reason) are not Pwnagotchi-specific. They are the entire defense for any Python that shells out:
The four moves (resolve binaries with `shutil.which()` at init, pass argv as lists, validate inputs at the trust boundary, annotate `# nosec` per-call with a structural reason) are the entire defense for any Python that shells out, Pwnagotchi or not:

- A homelab MCP server that runs `docker exec` on user-supplied container names.
- A CI script that calls `kubectl` against a cluster name from environment.
Expand Down
2 changes: 1 addition & 1 deletion content/telemetry-turns-predatory.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Internet censorship measurement is inherently difficult, and the sharpest limita

## Conclusion

The technology discussed here is not unique to Venezuela. It is the same telemetry, the same identity graphs, the same pipelines we build in every SOC on every continent. What changes is who operates them and against whom.
Nothing in the technology discussed here is unique to Venezuela: the same telemetry, the same identity graphs, the same pipelines we build in every SOC on every continent. What changes is who operates them and against whom.

The question I want you to carry into your next design review is no longer only *is this design secure?* It is *who does this design protect, and who does it expose?*

Expand Down
2 changes: 1 addition & 1 deletion content/testing-an-iam-analyzer-against-its-own-claims.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Cover: images/covers/testing-an-iam-analyzer-against-its-own-claims.png

I built a client-side IAM blast-radius analyzer. You paste an AWS IAM policy and it shows the potential blast radius - escalation paths, role-assumption reach, data exposure - entirely in the browser. The page makes no network calls at all, and loads no third-party scripts; a strict Content-Security-Policy (`default-src 'self'`, `connect-src 'none'`, `form-action 'none'`) is layered on top as defense in depth, so the policy never leaves the tab.

Building it was the easy half. The half that decides whether anyone should trust it was validation, and the useful results were not the attacks it caught. They were the two places the tool was confidently wrong while every test stayed green.
Building it was the easy half. The half that decides whether anyone should trust it was validation, and the most useful results were the two places the tool was confidently wrong while every test stayed green, more than any attack it caught.

## A green build that was still wrong

Expand Down
4 changes: 2 additions & 2 deletions content/trust-decay-adversarial-hiring.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ As presentation becomes commoditized through AI, "looking the part" no longer se

### 3. Upstream Trust Migration

Because the public pipeline is compromised, hiring teams are retreating to "pre-validated" channels. This explains the heavy reliance on internal referrals and known networks. It's not necessarily cronyism; it's an architectural necessity to find signal in a sea of noise.
Because the public pipeline is compromised, hiring teams are retreating to "pre-validated" channels. This explains the heavy reliance on internal referrals and known networks. Less cronyism than architectural necessity: it is how teams find signal in a sea of noise.

---

Expand Down Expand Up @@ -78,4 +78,4 @@ If we treat the job market as a security problem, the solution becomes clear. Th

The market isn't "broken" - it has simply changed its objective function. It no longer prioritizes finding the best; it prioritizes avoiding the unverified.

Success in 2026 and beyond isn't about having the most optimized resume. It's about being the most difficult to doubt. In a world of automated noise, reliability is the only signal that scales.
Success in 2026 and beyond belongs to whoever is the most difficult to doubt, not whoever has the most optimized resume. In a world of automated noise, reliability is the only signal that scales.
2 changes: 1 addition & 1 deletion content/venezuela-twitter-proxy-osint-final.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Returned expected Twitter IPs (199.59.x.x), but users in Venezuela were silently
190.202.80.20
```

This IP **served Twitter content** but was not operated by Twitter Inc.
This IP **served Twitter content** without being operated by Twitter Inc.

It's unclear whether this redirection was caused by ISP DNS override, local resolver poisoning, or upstream hijack, but the net effect was consistent: Twitter domains were silently redirected to non-Twitter infrastructure under state control.

Expand Down
2 changes: 1 addition & 1 deletion scripts/proposed-github-profile-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ GitHub issues on any of my repos, or reach out through this profile.

---

> Security is not a feature. It is infrastructure.
> Security is infrastructure, not a feature.
Loading