Skip to main content
INS // Insights

Zero Trust: Eliminating Long-Lived Credentials

Updated March 2026 · 9 min read

Every stored credential is a liability. Every API key committed to a repository is a breach waiting to happen. Every long-lived token is an attack surface that grows more dangerous with time. The zero trust approach to credentials isn't about better secret management — it's about eliminating secrets entirely.

At Rutagon, we've architected production systems across regulated environments — from defense platforms to federal CI/CD pipelines — where a single leaked credential could compromise national security. Our approach is simple: if a credential exists, it can be stolen. So we don't create them.

This article breaks down the architecture patterns we use to eliminate long-lived credentials from every layer of the stack: CI/CD pipelines, cloud workloads, cross-account access, database connections, and third-party integrations.

The Problem with Secrets Management

Traditional secrets management treats the symptom, not the disease. Tools like HashiCorp Vault, AWS Secrets Manager, and Azure Key Vault are excellent — but they still store secrets. They rotate them, encrypt them, audit access to them. But the secret still exists somewhere, and that somewhere is a target.

Consider the attack surface of a typical production system:

  • CI/CD service account keys stored in pipeline variables
  • AWS access keys for deployment automation
  • Database connection strings with embedded passwords
  • API keys for third-party services
  • SSH keys for server access
  • Container registry credentials

Each one of these is a static artifact that can be exfiltrated, shared, or compromised. Rotation helps, but rotation windows create their own vulnerabilities — and most teams don't rotate frequently enough.

OIDC-Based CI/CD Authentication

The first and highest-impact change is eliminating stored cloud credentials from CI/CD pipelines. OpenID Connect (OIDC) federation allows your CI/CD provider to authenticate directly with your cloud provider using short-lived, automatically rotated tokens.

How It Works

Instead of storing an AWS access key in GitLab CI variables, the pipeline requests a JWT token from the CI/CD provider's OIDC endpoint. AWS validates this token against a pre-configured trust policy and issues temporary credentials scoped to exactly the permissions needed for that pipeline run.

Here's the AWS IAM trust policy that enables this pattern:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::oidc-provider/gitlab.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "gitlab.com:sub": "project_path:rutagon/infrastructure:ref_type:branch:ref:main"
        },
        "StringLike": {
          "gitlab.com:aud": "https://gitlab.com"
        }
      }
    }
  ]
}

The Condition block is critical. It constrains which GitLab project and branch can assume this role. Without it, any GitLab project could potentially authenticate — defeating the purpose entirely.

GitLab CI Configuration

The pipeline configuration is cleaner without stored credentials:

deploy_production:
  stage: deploy
  image: amazon/aws-cli:latest
  id_tokens:
    GITLAB_OIDC_TOKEN:
      aud: https://gitlab.com
  script:
    - >
      export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s"
      $(aws sts assume-role-with-web-identity
      --role-arn ${DEPLOY_ROLE_ARN}
      --role-session-name "gitlab-ci-${CI_PIPELINE_ID}"
      --web-identity-token ${GITLAB_OIDC_TOKEN}
      --duration-seconds 900
      --query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]"
      --output text))
    - aws s3 sync ./dist s3://${BUCKET_NAME}/
    - aws cloudfront create-invalidation --distribution-id ${CF_DIST_ID} --paths "/*"
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Notice the --duration-seconds 900 — fifteen minutes. The credentials exist only for the duration of the deployment, and they're scoped to exactly the permissions defined in the IAM role. No stored keys, no rotation schedules, no leaked secrets in pipeline logs.

Workload Identity Federation

OIDC isn't just for CI/CD. The same pattern extends to every workload that needs cloud access.

AWS IAM Roles for Service Accounts (IRSA)

In Kubernetes environments, pods authenticate to AWS using IAM Roles for Service Accounts. Each pod gets a projected service account token that AWS STS validates against an OIDC provider — the EKS cluster itself.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: data-processor
  namespace: production
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::role/data-processor-role
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: data-processor
spec:
  template:
    spec:
      serviceAccountName: data-processor
      containers:
        - name: processor
          image: registry.example.com/data-processor:v2.4.1
          env:
            - name: AWS_DEFAULT_REGION
              value: us-west-2

The pod never sees credentials. The AWS SDK detects the projected token, exchanges it with STS, and receives temporary credentials automatically. This is invisible to application code — no credential loading, no environment variable parsing, no secret file mounting.

Cross-Account Lambda Architecture

Multi-account AWS architectures are common in regulated environments — separate accounts for development, staging, production, security, and logging. Lambda functions frequently need cross-account access, and the traditional approach of storing access keys is both insecure and fragile.

Instead, we use execution roles with cross-account assume-role chains:

import boto3

def lambda_handler(event, context):
    sts_client = boto3.client('sts')

    assumed_role = sts_client.assume_role(
        RoleArn='arn:aws:iam::role/cross-account-data-reader',
        RoleSessionName=f'lambda-{context.function_name}',
        DurationSeconds=900
    )

    credentials = assumed_role['Credentials']

    s3_client = boto3.client(
        's3',
        aws_access_key_id=credentials['AccessKeyId'],
        aws_secret_access_key=credentials['SecretAccessKey'],
        aws_session_token=credentials['SessionToken']
    )

    response = s3_client.get_object(
        Bucket='target-account-data-bucket',
        Key=event['object_key']
    )
    return response['Body'].read().decode('utf-8')

The Lambda execution role in the source account has permission to assume the target role. The target account's role trusts the source account's Lambda execution role. No credentials are stored anywhere — the entire chain is IAM policy.

Database Connections Without Passwords

IAM authentication extends to databases. Both RDS and Aurora support IAM database authentication, which replaces password-based connections with token-based authentication:

import boto3

def get_db_connection():
    rds_client = boto3.client('rds')

    token = rds_client.generate_db_auth_token(
        DBHostname='production-db.cluster-abc123.us-west-2.rds.amazonaws.com',
        Port=5432,
        DBUsername='app_service_account',
        Region='us-west-2'
    )

    import psycopg2
    conn = psycopg2.connect(
        host='production-db.cluster-abc123.us-west-2.rds.amazonaws.com',
        port=5432,
        user='app_service_account',
        password=token,
        database='production',
        sslmode='verify-full',
        sslrootcert='/opt/certs/rds-ca-bundle.pem'
    )
    return conn

The token is valid for 15 minutes, generated on-demand, and requires no stored password. The database user is configured to authenticate via IAM rather than a password, and the IAM role attached to the workload controls which database users can be impersonated.

Snowflake Key Pair Authentication

For data warehouse connections, Snowflake supports key pair authentication with external OAuth or key pair rotation. In production, we use Snowflake's external OAuth integration with the same OIDC patterns:

CREATE SECURITY INTEGRATION pipeline_oauth
  TYPE = EXTERNAL_OAUTH
  ENABLED = TRUE
  EXTERNAL_OAUTH_TYPE = CUSTOM
  EXTERNAL_OAUTH_ISSUER = 'https://sts.amazonaws.com'
  EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM = 'sub'
  EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE = 'LOGIN_NAME'
  EXTERNAL_OAUTH_JWS_KEYS_URL = 'https://sts.amazonaws.com/.well-known/jwks.json';

The application authenticates to AWS (via IRSA or OIDC), receives a token, and presents that token to Snowflake. No Snowflake password exists anywhere in the system.

Container Registry Authentication

Container registries are another common source of stored credentials. ECR solves this natively — if your workload already has an IAM role, it can pull images without any additional credentials:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      serviceAccountName: app-service-account
      containers:
        - name: app
          image: .dkr.ecr.us-west-2.amazonaws.com/app:v1.2.0

For CI/CD pipelines, the OIDC-federated role includes ECR pull permissions. The pipeline authenticates to ECR using the same temporary credentials it uses for everything else.

The Architecture Pattern

The unifying principle across all these patterns is identity federation. Instead of creating and distributing credentials, you establish trust relationships between identity providers and resource providers. The workload proves its identity through its runtime context — which CI/CD pipeline is running, which Kubernetes pod is executing, which Lambda function is invoked — and receives exactly the permissions it needs for exactly as long as it needs them.

This aligns with our core value of Security Is Architecture. Security isn't a layer you bolt on after the system is built. It's a fundamental architectural decision that shapes every component, every connection, every deployment.

Monitoring and Compliance

Eliminating credentials simplifies compliance dramatically. There are no secrets to rotate, no key age policies to enforce, no credential leak scanners to run. Instead, you monitor IAM role assumption events in CloudTrail:

  • Every AssumeRoleWithWebIdentity call is logged with the OIDC token claims
  • Every AssumeRole call includes the source identity and session name
  • Every database authentication token generation is auditable
  • Failed authentication attempts are immediately visible

This provides a complete, immutable audit trail that maps every access event to a specific workload, pipeline, or service — exactly what auditors in regulated environments need.

Getting Started

The migration path is incremental. Start with CI/CD pipelines — they're the highest-risk, highest-impact target. Then move to Kubernetes workloads with IRSA. Then database connections. Each step removes a category of stored credentials without changing application logic.

We've deployed this pattern across production systems serving millions of requests in environments where security isn't optional — it's the entire point. The result is an architecture where there are no credentials to steal, no secrets to leak, and no keys to rotate.

How does OIDC authentication work for CI/CD pipelines?

OIDC (OpenID Connect) allows CI/CD providers like GitLab or GitHub Actions to generate short-lived JWT tokens that cloud providers like AWS validate against a trust policy. Instead of storing long-lived access keys, the pipeline requests temporary credentials scoped to specific permissions and valid for minutes rather than months.

What is workload identity federation?

Workload identity federation is a pattern where a workload proves its identity through its runtime context — the Kubernetes service account it runs as, the CI/CD project it belongs to, or the Lambda execution role assigned to it — rather than presenting stored credentials. Cloud providers validate this identity and issue temporary, scoped access tokens.

Can IAM database authentication replace all database passwords?

For AWS RDS and Aurora, yes. IAM database authentication generates 15-minute tokens that replace traditional passwords entirely. The database user is configured to authenticate via IAM, and the workload's IAM role determines which database users it can connect as. This eliminates stored database passwords from application configuration.

How does eliminating credentials affect compliance auditing?

It simplifies auditing significantly. Instead of tracking credential rotation schedules, key ages, and access patterns across multiple secret stores, auditors can review a single CloudTrail log that maps every access event to a specific workload identity. Every role assumption, token generation, and authentication event is immutably logged with full context.

What's the first step to eliminating stored credentials?

Start with CI/CD pipelines. They typically contain the most sensitive credentials (cloud provider access keys with broad permissions) and are the easiest to migrate to OIDC federation. Most major CI/CD platforms — GitLab, GitHub Actions, CircleCI — support OIDC natively, and the migration requires no application code changes.

Discuss your project with Rutagon

Contact Us →

Ready to discuss your project?

We deliver production-grade software for government, defense, and commercial clients. Let's talk about what you need.

Initiate Contact