Skip to content

Security: aws-samples/sample-kro-serverless-app-stack

Security

docs/SECURITY.md

Security Guide

This document covers security best practices for the Kro Serverless Application Stack.

Table of Contents

  1. Security Overview
  2. IAM Configuration
  3. Cognito Authentication
  4. API Gateway Security
  5. S3 Bucket Security
  6. Data Encryption
  7. Security Checklist

Security Overview

The Kro Serverless Application Stack implements defense-in-depth security:

┌─────────────────────────────────────────────────────────────────┐
│                     Security Layers                              │
├─────────────────────────────────────────────────────────────────┤
│  Layer 1: Network Security                                       │
│  ├── CloudFront with HTTPS only                                 │
│  ├── API Gateway with TLS 1.2+                                  │
│  └── VPC endpoints (optional)                                   │
├─────────────────────────────────────────────────────────────────┤
│  Layer 2: Authentication                                         │
│  ├── Cognito User Pool                                          │
│  ├── JWT token validation                                       │
│  └── OAuth 2.0 authorization code flow                          │
├─────────────────────────────────────────────────────────────────┤
│  Layer 3: Authorization                                          │
│  ├── IAM roles with least privilege                             │
│  ├── Cognito groups for RBAC                                    │
│  └── Resource-based policies                                    │
├─────────────────────────────────────────────────────────────────┤
│  Layer 4: Data Protection                                        │
│  ├── DynamoDB encryption at rest                                │
│  ├── S3 server-side encryption                                  │
│  └── TLS in transit                                             │
└─────────────────────────────────────────────────────────────────┘

IAM Configuration

Principle of Least Privilege

All IAM roles follow least privilege principles:

Lambda Execution Role

# Minimal permissions for Lambda function
PolicyDocument:
  Version: "2012-10-17"
  Statement:
    # DynamoDB access - specific table only
    - Effect: Allow
      Action:
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
        - dynamodb:Query
        - dynamodb:Scan
      Resource:
        - "arn:aws:dynamodb:*:*:table/${TABLE_NAME}"
        - "arn:aws:dynamodb:*:*:table/${TABLE_NAME}/index/*"
    
    # CloudWatch Logs - function-specific
    - Effect: Allow
      Action:
        - logs:CreateLogGroup
        - logs:CreateLogStream
        - logs:PutLogEvents
      Resource:
        - "arn:aws:logs:*:*:log-group:/aws/lambda/${FUNCTION_NAME}:*"

S3 Uploader Role

# Limited to specific bucket and prefix
PolicyDocument:
  Version: "2012-10-17"
  Statement:
    - Effect: Allow
      Action:
        - s3:PutObject
        - s3:GetObject
        - s3:ListBucket
      Resource:
        - "arn:aws:s3:::${BUCKET_NAME}"
        - "arn:aws:s3:::${BUCKET_NAME}/${PREFIX}/*"

IRSA (IAM Roles for Service Accounts)

Kubernetes ServiceAccounts are linked to IAM roles:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: s3-uploader-sa
  annotations:
    eks.amazonaws.com/role-arn: "arn:aws:iam::ACCOUNT:role/s3-uploader-role"

Trust Policy:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::ACCOUNT:oidc-provider/OIDC_PROVIDER"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "OIDC_PROVIDER:sub": "system:serviceaccount:NAMESPACE:SA_NAME"
      }
    }
  }]
}

Cognito Authentication

User Pool Configuration

# Secure User Pool settings
spec:
  forProvider:
    # Password policy
    policies:
      passwordPolicy:
        minimumLength: 12
        requireLowercase: true
        requireUppercase: true
        requireNumbers: true
        requireSymbols: true
        temporaryPasswordValidityDays: 7
    
    # MFA configuration
    mfaConfiguration: OPTIONAL
    
    # Account recovery
    accountRecoverySetting:
      recoveryMechanisms:
        - name: verified_email
          priority: 1
    
    # User verification
    autoVerifiedAttributes:
      - email

App Client Security

# Secure App Client settings
spec:
  forProvider:
    # Generate client secret
    generateSecret: true
    
    # OAuth flows
    allowedOAuthFlows:
      - code  # Authorization code flow (most secure)
    
    # Prevent implicit flow in production
    allowedOAuthFlowsUserPoolClient: true
    
    # Token validity
    idTokenValidity: 60      # 1 hour
    accessTokenValidity: 60  # 1 hour
    refreshTokenValidity: 30 # 30 days
    tokenValidityUnits:
      idToken: minutes
      accessToken: minutes
      refreshToken: days
    
    # Callback URLs (use HTTPS only in production)
    callbackURLs:
      - "https://*.example.com/callback"
    logoutURLs:
      - "https://*.example.com"

User Groups for RBAC

# Define groups with precedence
groups:
  - name: platform-admins
    precedence: 1
    description: Full platform administration
  
  - name: developers
    precedence: 2
    description: Application developers
  
  - name: read-only
    precedence: 3
    description: Read-only access

API Gateway Security

JWT Authorizer

# API Gateway JWT Authorizer
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: Authorizer
spec:
  apiID: ${API_ID}
  authorizerType: JWT
  identitySource:
    - "$request.header.Authorization"
  jwtConfiguration:
    issuer: "https://cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}"
    audience:
      - "${CLIENT_ID}"

Route Authorization

# Protected routes require JWT
apiVersion: apigatewayv2.services.k8s.aws/v1alpha1
kind: Route
spec:
  apiID: ${API_ID}
  routeKey: "GET /items"
  authorizationType: JWT
  authorizerID: ${AUTHORIZER_ID}

CORS Configuration

# Restrictive CORS policy
corsConfiguration:
  allowOrigins:
    - "https://*.example.com"
  allowMethods:
    - GET
    - POST
    - PUT
    - DELETE
    - OPTIONS
  allowHeaders:
    - Authorization
    - Content-Type
  maxAge: 300

S3 Bucket Security

Origin Access Control (OAC)

CloudFront accesses S3 through OAC, not public access:

# OAC configuration
apiVersion: cloudfront.services.k8s.aws/v1alpha1
kind: OriginAccessControl
spec:
  originAccessControlConfig:
    name: "hkc-sample-oac"
    signingBehavior: always
    signingProtocol: sigv4
    originAccessControlOriginType: s3

Bucket Policy

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowCloudFrontServicePrincipalReadOnly",
    "Effect": "Allow",
    "Principal": {
      "Service": "cloudfront.amazonaws.com"
    },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::BUCKET_NAME/*",
    "Condition": {
      "StringEquals": {
        "AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT:distribution/DIST_ID"
      }
    }
  }]
}

Block Public Access

# Block all public access
publicAccessBlockConfiguration:
  blockPublicAcls: true
  blockPublicPolicy: true
  ignorePublicAcls: true
  restrictPublicBuckets: true

Data Encryption

DynamoDB Encryption

# Server-side encryption enabled by default
spec:
  sseSpecification:
    enabled: true
    sseType: KMS  # Use AWS managed key or CMK

S3 Encryption

# Server-side encryption
spec:
  serverSideEncryptionConfiguration:
    rules:
      - applyServerSideEncryptionByDefault:
          sseAlgorithm: AES256
          # Or use KMS:
          # sseAlgorithm: aws:kms
          # kmsMasterKeyID: "arn:aws:kms:..."

TLS in Transit

  • CloudFront: TLS 1.2+ enforced
  • API Gateway: TLS 1.2+ enforced
  • Lambda to DynamoDB: TLS by default
  • Lambda to S3: TLS by default

Security Checklist

Pre-Deployment

  • ACM certificates issued and validated
  • Route 53 hosted zone configured
  • OIDC provider configured for EKS
  • IAM roles created with least privilege
  • Cognito User Pool configured with strong password policy

Deployment

  • Environment variables secured (not in Git)
  • Secrets stored in Kubernetes Secrets or AWS Secrets Manager
  • HTTPS enforced for all endpoints
  • JWT authorizer configured for API Gateway
  • S3 bucket policies restrict access to CloudFront only

Post-Deployment

  • CloudWatch Logs enabled for Lambda
  • API Gateway access logging enabled
  • CloudFront access logging enabled (optional)
  • Cognito user activity monitored
  • IAM Access Analyzer reviewed

Ongoing

  • Regular security audits
  • Dependency updates
  • Certificate renewal monitoring
  • Access key rotation (if applicable)
  • User access reviews

Security Best Practices

1. Never Commit Secrets

# Add to .gitignore
scripts/env.sh
*.env
*.secret

2. Use Secrets Manager for Sensitive Data

# Store secrets
aws secretsmanager create-secret \
  --name "hkc-sample/cognito-client-secret" \
  --secret-string "YOUR_SECRET"

3. Enable CloudTrail

# Monitor API calls
aws cloudtrail create-trail \
  --name hkc-sample-trail \
  --s3-bucket-name your-cloudtrail-bucket

4. Implement WAF (Optional)

# Web Application Firewall for CloudFront
apiVersion: wafv2.services.k8s.aws/v1alpha1
kind: WebACL
spec:
  scope: CLOUDFRONT
  defaultAction:
    allow: {}
  rules:
    - name: AWSManagedRulesCommonRuleSet
      priority: 1
      overrideAction:
        none: {}
      statement:
        managedRuleGroupStatement:
          vendorName: AWS
          name: AWSManagedRulesCommonRuleSet

5. Regular Security Reviews

  • Review IAM policies quarterly
  • Audit Cognito user access monthly
  • Check for unused resources
  • Update dependencies regularly

Compliance Considerations

SOC 2

  • Enable CloudTrail logging
  • Implement access controls
  • Document security procedures

GDPR

  • Implement data retention policies
  • Enable encryption at rest
  • Document data processing

HIPAA

  • Use KMS for encryption
  • Enable audit logging
  • Implement access controls

References

There aren’t any published security advisories