diff --git a/content/hardening-k8s.md b/content/hardening-k8s.md index 599c348d..3857b4d7 100644 --- a/content/hardening-k8s.md +++ b/content/hardening-k8s.md @@ -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 @@ -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). diff --git a/content/iam-blast-radius-architecture-problem.md b/content/iam-blast-radius-architecture-problem.md index 5b55a8c0..83078d07 100644 --- a/content/iam-blast-radius-architecture-problem.md +++ b/content/iam-blast-radius-architecture-problem.md @@ -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. @@ -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. @@ -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. @@ -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. diff --git a/content/oom-killer-process-prioritization.md b/content/oom-killer-process-prioritization.md index bf604ad8..9bd81c97 100644 --- a/content/oom-killer-process-prioritization.md +++ b/content/oom-killer-process-prioritization.md @@ -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. diff --git a/content/pages/about.md b/content/pages/about.md index e8ae827e..d93c416c 100644 --- a/content/pages/about.md +++ b/content/pages/about.md @@ -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. diff --git a/content/paved-road-adoption-as-control.md b/content/paved-road-adoption-as-control.md index 13e66602..887bcc9b 100644 --- a/content/paved-road-adoption-as-control.md +++ b/content/paved-road-adoption-as-control.md @@ -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. @@ -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 diff --git a/content/pwnagotchi-plugin-bandit-hardening.md b/content/pwnagotchi-plugin-bandit-hardening.md index 39ea9149..8b7b10a7 100644 --- a/content/pwnagotchi-plugin-bandit-hardening.md +++ b/content/pwnagotchi-plugin-bandit-hardening.md @@ -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: @@ -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. diff --git a/content/telemetry-turns-predatory.md b/content/telemetry-turns-predatory.md index fe4ddd7c..3fd6356a 100644 --- a/content/telemetry-turns-predatory.md +++ b/content/telemetry-turns-predatory.md @@ -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?* diff --git a/content/testing-an-iam-analyzer-against-its-own-claims.md b/content/testing-an-iam-analyzer-against-its-own-claims.md index 94cd90ed..75f88412 100644 --- a/content/testing-an-iam-analyzer-against-its-own-claims.md +++ b/content/testing-an-iam-analyzer-against-its-own-claims.md @@ -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 diff --git a/content/trust-decay-adversarial-hiring.md b/content/trust-decay-adversarial-hiring.md index 04960c57..87b74ee2 100644 --- a/content/trust-decay-adversarial-hiring.md +++ b/content/trust-decay-adversarial-hiring.md @@ -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. --- @@ -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. diff --git a/content/venezuela-twitter-proxy-osint-final.md b/content/venezuela-twitter-proxy-osint-final.md index 5326a364..04ce297d 100644 --- a/content/venezuela-twitter-proxy-osint-final.md +++ b/content/venezuela-twitter-proxy-osint-final.md @@ -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. diff --git a/scripts/proposed-github-profile-README.md b/scripts/proposed-github-profile-README.md index 2a36c5a6..e5ecb721 100644 --- a/scripts/proposed-github-profile-README.md +++ b/scripts/proposed-github-profile-README.md @@ -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.