cloud-foundation-principles/skills/zero-static-credentials/SKILL.md
This skill should be used when the user is configuring SSO, setting up CI/CD authentication, designing OIDC federation, eliminating SSH keys or VPN, managing human or machine identity, or discussing credential lifecycle. Covers core access patterns (human SSO, pipeline OIDC, operator session-based), federated identity, workload identity federation, and the elimination of static API keys, SSH keys, and VPN files.
npx skillsauth add oborchers/fractional-cto zero-static-credentialsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Static credentials are the number one cause of cloud security breaches. An SSH key on a developer's laptop, a VPN configuration file shared via Slack, cloud provider access keys stored in a CI/CD secret -- each one is a ticking time bomb. Static credentials cannot be revoked instantly across all consumers. They accumulate silently over time. They are almost always over-permissioned. And when they leak (not if), the blast radius is unknowable because nobody tracks where they were copied.
Zero static credentials applies fully to cloud provider credentials -- no long-lived AWS access keys, no GCP service account key files, no SSH key pairs, no VPN configuration files. Every cloud access path uses short-lived, automatically rotated, centrally revocable tokens. Three core access patterns cover the vast majority of cloud interactions.
Third-party service credentials (Supabase keys, Stripe API keys, NPM tokens, SendGrid keys) are a different category. Most third-party services do not support OIDC or workload identity federation, so these credentials must be stored somewhere. CI/CD secret stores and cloud secrets managers are the correct places -- they are encrypted, access-controlled, and auditable. For these credentials, the goal is not elimination but proper management: store in an encrypted secret store, scope to minimum permissions, and rotate on a schedule where the service supports it.
These three patterns cover human access, automated pipelines, and operational debugging. If a proposed access method does not use short-lived, centrally revocable tokens, it is a security liability.
| Pattern | Who | How | Token Lifetime | |---------|-----|-----|----------------| | Human -> SSO -> Cloud | Developers, operators, admins | Federated identity (SSO) to cloud console and CLI | 1-12 hours (session) | | Pipeline -> OIDC -> Cloud | CI/CD workflows, automated deploys | Workload identity federation (OIDC) | Minutes (per-job) | | Operator -> Session Manager -> Instance | On-call engineers, debuggers | Session-based instance access (no SSH) | Duration of session |
| Eliminated | Replaced By | Why It Matters | |-----------|-------------|----------------| | IAM access keys (API key + secret) | SSO temporary credentials | Access keys are permanent until manually rotated; SSO tokens expire automatically | | SSH key pairs (.pem files) | Session-based instance access | SSH keys are copied, shared, never rotated, and impossible to audit | | VPN configuration (.ovpn files) | Private connectivity endpoints + session manager | VPN files are shared, credentials embedded, and revocation requires redistribution | | Stored CI/CD secrets (cloud credentials) | OIDC federation | CI/CD secrets are long-lived, often over-permissioned, and shared across pipelines | | Database passwords in developer machines | Team-based roles + IAM authentication | Individual credentials create sprawl; IAM auth uses SSO session |
Every human accesses the cloud through a single sign-on provider. When an employee leaves, disabling their SSO account instantly revokes all cloud access across all accounts and services.
External Identity Provider (Google Workspace, Okta, Entra ID)
|
| SAML / OIDC federation
|
Cloud Identity Service (IAM Identity Center, Cloud Identity, Entra ID)
|
+-- Permission Set: Admin -> Full access (2-3 people)
+-- Permission Set: Developer -> Full dev, read-only prod
+-- Permission Set: ReadOnly -> Audit and compliance
|
+-- Account: dev -> Developer: full access
+-- Account: prod -> Developer: read-only (targeted exceptions)
+-- Account: sandbox -> Developer: full access
+-- Account: security -> Admin only
External IdP as single source of truth: Do not manage users in the cloud provider. Your identity provider (Google Workspace, Okta, Microsoft Entra ID) is the single source of truth. Onboarding means adding a user to the IdP. Offboarding means disabling the IdP account. No second step required.
Three permission tiers (Admin, Developer, ReadOnly): See the multi-account-from-day-one skill for the full tier definitions and per-account access scoping.
MFA enforced at the IdP: Multi-factor authentication is configured in the identity provider, not in the cloud. This ensures MFA covers all access paths, not just the cloud console.
BAD: Static credentials for human access
- Developer has AWS_ACCESS_KEY_ID in ~/.aws/credentials
- Key was created 18 months ago, never rotated
- Key has AdministratorAccess (over-permissioned)
- Developer left the company 3 months ago (key still active)
- Nobody knows this key exists
GOOD: SSO-based access
- Developer authenticates via Google Workspace SSO
- Session token expires in 8 hours
- Access scoped to DeveloperAccess permission set
- Developer leaves -> Google account disabled -> instant cloud revocation
- All sessions auditable via IdP logs
CI/CD pipelines authenticate to the cloud using workload identity federation (OIDC). The CI/CD platform (GitHub Actions, GitLab CI, etc.) provides a signed JWT token. The cloud provider validates the token and issues short-lived credentials scoped to a specific role. No secrets are stored anywhere.
CI/CD Platform (GitHub Actions, GitLab CI, etc.)
|
| Signed JWT (OIDC token)
| Claims: repo, branch, workflow, environment
|
Cloud Provider OIDC Trust
|
| Validates token signature + claims
| Issues short-lived credentials (minutes)
|
+-- Role: InfraDeployRole -> Terraform apply permissions
+-- Role: AppDeployRole -> Container push + service update
+-- Role: ReadOnlyRole -> Terraform plan only (PRs)
The OIDC trust policy controls exactly which repositories and branches can assume which roles. This is the critical security boundary.
# OIDC provider (created once per account)
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
# Role assumable ONLY by specific repositories
resource "aws_iam_role" "github_actions" {
name = "GithubActionsDeployRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringLike = {
"token.actions.githubusercontent.com:sub" = [
"repo:myorg/infrastructure:*",
"repo:myorg/api-service:*"
]
}
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
}
}
]
})
}
# .github/workflows/deploy.yml
name: Deploy Infrastructure
on:
push:
tags: ["v*"]
permissions:
id-token: write # Required for OIDC
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Authenticate via OIDC -- no stored secrets
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::role/GithubActionsDeployRole
aws-region: eu-west-1
# No access key, no secret key -- OIDC only
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.8.0"
- run: terraform init
- run: terraform apply -auto-approve
BAD: Static credentials in CI/CD
- AWS_ACCESS_KEY_ID stored as GitHub secret
- AWS_SECRET_ACCESS_KEY stored as GitHub secret
- Keys have AdministratorAccess
- Keys were created 2 years ago
- Same keys used across 15 repositories
- Cannot revoke without breaking all pipelines
GOOD: OIDC federation for cloud credentials
- No cloud credentials stored in CI/CD platform
- Each job gets unique, short-lived cloud credentials
- Role assumption restricted to specific repositories
- Token expires when the job ends
- Revoking access = update the trust policy (instant, no pipeline changes)
- Third-party keys (Supabase, Stripe, etc.) still use CI/CD secrets -- that is expected
When operators need to access a running instance (for debugging, log inspection, or emergency response), they use session-based access. No SSH keys. No VPN. No bastion host with a public IP. The session is authenticated via SSO, logged end-to-end, and terminates automatically.
Operator (authenticated via SSO)
|
| Cloud CLI / Console
|
Session Manager Service
|
| Encrypted session channel (no SSH, no port 22)
|
Target Instance (private subnet, no public IP)
|
+-- Session logged (every keystroke)
+-- Session expires automatically
+-- No SSH key on instance
+-- No SSH daemon required
| Traditional Approach | Problem | Session Manager Approach | |---------------------|---------|------------------------| | SSH key pair (.pem file) | Shared, copied, never rotated | SSO-authenticated session | | Bastion host with public IP | Attack surface, SSH brute force | No public IP, no SSH port | | VPN to reach private instances | VPN config files shared, complex setup | Direct session via cloud API | | Port forwarding via SSH tunnel | Key management, complex commands | Managed port forwarding via session |
For database access during debugging, use session-based port forwarding instead of SSH tunnels:
# Traditional (BAD): SSH tunnel with key file
ssh -i ~/.ssh/mykey.pem -L 5432:db.internal:5432 [email protected]
# Modern (GOOD): Session manager port forwarding (AWS example)
aws ssm start-session \
--target i-0abc123def456 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"portNumber":["5432"],"localPortNumber":["5432"],"host":["db.internal"]}'
# The operator is authenticated via SSO
# The session is logged
# No SSH key exists
# No VPN required
Run this audit regularly. Any "yes" answer is a security finding that must be remediated.
| Check | Expected | Finding If Present |
|-------|----------|-------------------|
| IAM access keys for any human user | None exist | Critical: static human credentials |
| SSH key pairs in any region | None exist | Critical: legacy access pattern |
| CI/CD platform secrets containing cloud credentials | None exist | Critical: static pipeline credentials |
| VPN configuration files | None exist | Important: replace with session manager |
| .pem files in any repository | None exist | Critical: committed credentials |
| Long-lived API keys for third-party services | Secrets manager with rotation | Important: move to secrets manager |
| Database passwords on developer machines | IAM auth or SSO-integrated access | Important: credential sprawl |
| Concept | AWS | GCP | Azure |
|---------|-----|-----|-------|
| Federated human identity | IAM Identity Center (SSO) | Cloud Identity + Workforce Identity Federation | Microsoft Entra ID |
| External IdP integration | SAML to IAM Identity Center | SAML/OIDC to Cloud Identity | SAML/OIDC to Entra ID |
| CI/CD OIDC federation | STS AssumeRoleWithWebIdentity | Workload Identity Federation | Federated Credentials |
| Session-based instance access | SSM Session Manager | IAP Tunneling | Azure Bastion |
| Credential-free CLI | aws sso login | gcloud auth login | az login |
| Port forwarding (no SSH) | SSM port forwarding | IAP TCP tunneling | Azure Bastion tunneling |
| Permission sets / roles | IAM Identity Center Permission Sets | IAM Roles + Conditions | Entra ID Roles + PIM |
| Temporary credential issuance | STS (Security Token Service) | Service Account Key alternatives | Managed Identity tokens |
| Audit trail | CloudTrail | Cloud Audit Logs | Azure Activity Log |
Working implementations in examples/:
examples/oidc-federation.md -- Complete OIDC setup for CI/CD with trust policies, role definitions, and workflow configurationexamples/sso-permission-sets.md -- SSO configuration with three permission tiers, group assignments, and per-account access scopingWhen designing or reviewing credential management:
.pem files exist in any repository or developer machinetools
This skill should be used when the user invokes any /plan-* command from the planning-tools plugin (/plan-context, /plan-master, /plan-open-questions, /plan-verify, /plan-tick, /plan-progress, /plan-delete), asks how Claude Code's plan files work, asks where plans are stored, asks to author or audit a multi-phase master planning document, asks how to walk through a plan's Open Questions interactively, asks how to write progress entries, or mentions ~/.claude/plans/ or .claude/planning-tools.local.md. Provides the index of planning-tools commands, the master-plan workflow lifecycle, the v0.3.0+ list-shape mandate (phases and questions as headings + bulleted scope items, never tables), the v0.3.2+ plain-bullet shape (no `- [ ]` checkboxes — heading emoji is the sole tick signal), the progress-entry methodology, and the mechanics of Claude Code's plan-mode file storage.
testing
This skill should be used by the plan-verifier agent and the /plan-verify command to audit a drafted master plan against a fixed checklist. Covers universal-core completeness, the v0.3.0+ no-tables-for-phases-or-questions rule, trigger-based section-coverage gaps, phase actionability (heading + per-phase TL;DR + bulleted scope + exit criteria), the v0.3.1+ per-phase TL;DR requirement, the v0.3.2+ plain-bullet scope shape (legacy `- [ ]`/`- [x]` accepted silently), the v0.3.3+ context-block shape (plan-level `**TL;DR:**` + bulleted metadata, legacy `>` blockquote accepted silently), integer phase numbering enforcement, dependency traceability, citation resolution, callout/evidence convention compliance, Open Questions placement, and the one-PR-per-master-plan rule. Single-owner of the audit checklist.
tools
This skill should be used when authoring, reviewing, or modifying a multi-phase master planning document via the planning-tools plugin (especially the /plan-master and /plan-verify commands). Codifies the universal core sections, trigger-based optional sections, integer-only phase numbering, Open Questions placement, one-PR-per-plan rule, status conventions, evidence attribution, callouts, cross-reference formats, the v0.3.0 list-shape mandate (phases and questions are heading + bulleted list, never markdown tables), the v0.3.1 per-phase TL;DR requirement (1–3 sentence what/why summary under each phase heading for glance-ability), the v0.3.2 plain-bullet scope shape (`- <action>` items, no `- [ ]` checkboxes — the phase status emoji is the sole tick signal), and the v0.3.3 context-block shape (a plan-level `**TL;DR:**` + a bulleted metadata list instead of a `>` blockquote; legacy blockquote blocks accepted silently). Project-agnostic — no ticket-prefix or plan-type taxonomy.
testing
This skill should be used when the user is adjusting spacing, padding, margins, content density, section gaps, vertical rhythm, or separation between elements. Also applies when reviewing whether a design feels cramped or too sparse, choosing between borders and whitespace for separation, or defining a spacing system. Covers the 4px/8px spacing system, macro vs micro whitespace, content density spectrum, separation techniques (whitespace > background shifts > borders), and vertical rhythm.