> ## Documentation Index
> Fetch the complete documentation index at: https://docs.artosai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# IAM Module

> IAM roles and policies for Artos application components using OIDC-based Kubernetes service accounts

## Overview

The IAM Module creates AWS IAM roles and policies for Artos application workloads running in EKS. Using IAM Roles for Service Accounts (IRSA), Kubernetes pods can securely access AWS services without hardcoded credentials. The module provisions separate roles for the backend application, Celery workers, and the AWS Load Balancer Controller, each with specific permissions tailored to their operational needs.

### Key Features

* **OIDC-Based Authentication**: Kubernetes service accounts assume IAM roles via OpenID Connect
* **No Hardcoded Credentials**: Temporary credentials automatically provided and rotated
* **Least Privilege Access**: Each component has only the permissions it needs
* **Secure Credential Management**: Access to Secrets Manager with KMS encryption
* **CloudWatch Integration**: Logging and metrics publication for observability

## IAM Roles for Service Accounts (IRSA)

### What is IRSA?

IAM Roles for Service Accounts (IRSA) is an AWS feature that allows Kubernetes service accounts to assume AWS IAM roles. This eliminates the need to store AWS credentials in your application code or Kubernetes secrets.

**How It Works**:

1. EKS cluster has an OIDC identity provider
2. Kubernetes service account is annotated with an IAM role ARN
3. When a pod uses this service account, AWS STS provides temporary credentials
4. Pod can make AWS API calls using these credentials
5. Credentials automatically expire and rotate

**Benefits**:

* **Security**: No long-lived credentials stored in cluster
* **Auditability**: All AWS API calls logged in CloudTrail
* **Isolation**: Each workload has separate IAM permissions
* **Automatic Rotation**: Credentials refresh automatically

## Core IAM Roles

### 1. Application Service Account Role

**Purpose**: Main backend API server that handles user requests, business logic, and data operations.

**Kubernetes Service Account**: `system:serviceaccount:default:app-service-account`

**AWS Permissions**:

#### S3 Access

```json theme={null}
{
  "Action": [
    "s3:GetObject",
    "s3:PutObject",
    "s3:DeleteObject",
    "s3:ListBucket"
  ]
}
```

**Use Cases**:

* Store and retrieve uploaded documents
* Save processed files and artifacts
* Manage user-generated content
* Access application assets and templates

#### Secrets Manager Access

```json theme={null}
{
  "Action": [
    "secretsmanager:GetSecretValue",
    "secretsmanager:DescribeSecret"
  ]
}
```

**Use Cases**:

* Retrieve database credentials
* Access API keys for third-party services
* Load application configuration secrets
* Get encryption keys for sensitive data

#### KMS Decryption

```json theme={null}
{
  "Action": [
    "kms:Decrypt",
    "kms:DescribeKey"
  ],
  "Condition": {
    "StringEquals": {
      "kms:ViaService": [
        "secretsmanager.{region}.amazonaws.com",
        "s3.{region}.amazonaws.com"
      ]
    }
  }
}
```

**Use Cases**:

* Decrypt Secrets Manager secrets encrypted with KMS
* Access S3 objects encrypted with customer-managed keys
* Read encrypted configuration data

<Note>
  **KMS Condition**: The `ViaService` condition ensures the KMS key can only be used by Secrets Manager and S3, not directly by the application. This adds an extra security layer.
</Note>

#### RDS IAM Authentication

```json theme={null}
{
  "Action": [
    "rds-db:connect"
  ]
}
```

**Use Cases**:

* Connect to RDS PostgreSQL using IAM authentication
* Eliminate database password management
* Automatic credential rotation via IAM

#### Amazon Bedrock Access

```json theme={null}
{
  "Action": [
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream",
    "bedrock:ListFoundationModels",
    "bedrock:GetFoundationModel"
  ]
}
```

**Use Cases**:

* Generate AI-powered responses
* Process natural language queries
* Summarize documents
* Classify and analyze content

#### CloudWatch Logs

```json theme={null}
{
  "Action": [
    "logs:CreateLogStream",
    "logs:CreateLogGroup",
    "logs:DescribeLogStreams",
    "logs:PutLogEvents"
  ]
}
```

**Use Cases**:

* Send application logs to CloudWatch
* Create structured log streams
* Enable centralized log aggregation

#### CloudWatch Metrics

```json theme={null}
{
  "Action": [
    "cloudwatch:PutMetricData",
    "cloudwatch:GetMetricStatistics",
    "cloudwatch:ListMetrics"
  ]
}
```

**Use Cases**:

* Publish custom application metrics
* Monitor business KPIs
* Track performance indicators
* Enable automated alerting

### 2. Celery Service Account Role

**Purpose**: Asynchronous task workers for background job processing, scheduled tasks, and long-running operations running within the Artos platform.

**Kubernetes Service Account**: `system:serviceaccount:default:celery-service-account`

**AWS Permissions**: Same as Application Service Account

The Celery workers require identical permissions to the main application because they:

* Process documents stored in S3
* Access the same secrets for database and API connectivity
* Use Bedrock for AI processing in background jobs
* Write logs and metrics to CloudWatch
* Connect to RDS for data operations

### 3. AWS Load Balancer Controller Role

**Purpose**: Kubernetes controller that manages AWS Application Load Balancers (ALBs) and Network Load Balancers (NLBs) for Ingress resources.

**Kubernetes Service Account**: `system:serviceaccount:kube-system:aws-load-balancer-controller`

**AWS Permissions**: Comprehensive Elastic Load Balancing and EC2 permissions

**Key Capabilities**:

#### Load Balancer Management

* Create and delete ALBs and NLBs
* Modify load balancer attributes
* Configure listeners and rules
* Manage SSL/TLS certificates

#### Target Group Operations

* Create target groups for Kubernetes services
* Register and deregister pod IPs as targets
* Configure health checks
* Manage target group attributes

#### Security Group Management

* Create security groups for load balancers
* Modify ingress/egress rules
* Tag resources for cluster association

#### Network Discovery

* Describe VPCs, subnets, and availability zones
* Query ENIs and instance metadata
* Discover ACM certificates for HTTPS

**How It Works**:

1. You create a Kubernetes Ingress resource
2. Load Balancer Controller detects the Ingress
3. Controller creates an ALB in your VPC
4. ALB routes traffic to Kubernetes service endpoints
5. Controller updates target groups as pods scale

**Example Ingress Resource**:

```yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: artos-api
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:...
spec:
  rules:
  - host: api.yourcompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: artos-api
            port:
              number: 8000
```

## Module Configuration

### Basic Configuration

```hcl theme={null}
module "iam" {
  source = "./modules/iam"

  cluster_name = "artos-production"
  
  # OIDC provider from EKS module
  oidc_provider_arn = module.eks.oidc_provider_arn
  oidc_provider_url = module.eks.oidc_provider_url
  
  # S3 bucket for application data
  s3_bucket_arn = module.s3.bucket_arn
  
  # Secrets Manager for application secrets
  secrets_manager_arns = [
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/db-credentials-abc123",
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api-keys-def456"
  ]
  
  # RDS database cluster
  db_cluster_identifier = module.rds.cluster_identifier
  
  # KMS key for encryption
  kms_key_arn = module.kms.key_arn
  
  # AWS account information
  aws_region     = "us-east-1"
  aws_account_id = "123456789012"
  
  tags = {
    Environment = "production"
  }
}
```

### Configuration with Multiple Secrets

<Note>
  For our deployment strategy only one set of secrets should be necessary.
</Note>

```hcl theme={null}
module "iam_production" {
  source = "./modules/iam"

  cluster_name = "artos-production"
  
  oidc_provider_arn = module.eks.oidc_provider_arn
  oidc_provider_url = module.eks.oidc_provider_url
  
  s3_bucket_arn = module.s3.bucket_arn
  
  # Multiple secrets for different components
  secrets_manager_arns = [
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/database/*",
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/redis/*",
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/smtp/*",
    "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/oauth/*"
  ]
  
  db_cluster_identifier = module.rds.cluster_identifier
  kms_key_arn          = module.kms.key_arn
  
  aws_region     = "us-east-1"
  aws_account_id = data.aws_caller_identity.current.account_id
  
  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}
```

## Security Best Practices

### 1. Principle of Least Privilege

Each IAM role grants only the minimum permissions required for its workload.

**Example Custom Policy**:

```hcl theme={null}
resource "aws_iam_policy" "custom_app_policy" {
  name = "${var.cluster_name}-custom-app-policy"
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject"
        ]
        Resource = ["${var.s3_bucket_arn}/*"]
      }
      # Only include services you actually use
    ]
  })
}
```

### 2. Resource-Level Restrictions

Where possible, restrict permissions to specific resources rather than using wildcards.

**Good - Specific Resources**:

```json theme={null}
{
  "Resource": [
    "arn:aws:s3:::artos-production-data",
    "arn:aws:s3:::artos-production-data/*"
  ]
}
```

**Avoid - Wildcard Resources** (only when necessary):

```json theme={null}
{
  "Resource": "*"
}
```

### 3. KMS Key Conditions

Always use `ViaService` conditions with KMS permissions to ensure keys can only be used by specific AWS services.

```json theme={null}
{
  "Condition": {
    "StringEquals": {
      "kms:ViaService": [
        "secretsmanager.us-east-1.amazonaws.com",
        "s3.us-east-1.amazonaws.com"
      ]
    }
  }
}
```

### 4. Secrets Manager Naming Conventions

Use consistent naming patterns for secrets to enable prefix-based permissions.

**Recommended Structure**:

```
{environment}/{component}/{secret-name}

Examples:
- prod/database/master-credentials
- prod/redis/connection-string
- prod/smtp/api-key
```

**ARN Pattern Matching**:

```hcl theme={null}
secrets_manager_arns = [
  "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/*"
]
```

## Troubleshooting

### Pod Cannot Assume IAM Role

**Symptoms**: Application logs show `Unable to locate credentials` or `AccessDenied` errors.

**Troubleshooting Steps**:

1. **Verify Service Account Annotation**:

```bash theme={null}
kubectl get serviceaccount app-service-account -o yaml

# Should show:
# metadata:
#   annotations:
#     eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/...
```

2. **Check Pod Environment Variables**:

```bash theme={null}
kubectl exec -it <pod-name> -- env | grep AWS

# Should show:
# AWS_ROLE_ARN=arn:aws:iam::123456789012:role/...
# AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
```

3. **Verify IAM Trust Relationship**:

```bash theme={null}
aws iam get-role --role-name artos-production-app-service-account

# Check that trust policy includes correct OIDC provider and service account
```

4. **Test IAM Role Assumption**:

```bash theme={null}
# Inside pod
aws sts get-caller-identity

# Should return:
# {
#   "UserId": "AROA...:eks-...",
#   "Account": "123456789012",
#   "Arn": "arn:aws:sts::123456789012:assumed-role/artos-production-app-service-account/..."
# }
```

### Access Denied to AWS Service

**Symptoms**: `AccessDenied` or `UnauthorizedOperation` errors when accessing specific AWS services.

**Solutions**:

1. **Verify IAM Policy Permissions**:

```bash theme={null}
aws iam get-role-policy \
  --role-name artos-production-app-service-account \
  --policy-name artos-production-app-policy
```

2. **Check Resource ARNs Match**:
   Ensure S3 bucket ARNs, Secrets Manager ARNs, and KMS key ARNs in your IAM policy match the actual resources.

3. **Test Permissions from Pod**:

```bash theme={null}
# Test S3 access
kubectl exec -it <pod-name> -- aws s3 ls s3://artos-production-data

# Test Secrets Manager access
kubectl exec -it <pod-name> -- aws secretsmanager get-secret-value \
  --secret-id prod/db-credentials
```

### KMS Decryption Errors

**Symptoms**: `KMS.DisabledException` or `AccessDeniedException` when accessing encrypted secrets.

**Solutions**:

1. **Verify KMS Key Policy**:
   Ensure the KMS key policy allows the IAM role to decrypt:

```json theme={null}
{
  "Sid": "AllowApplicationDecryption",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::123456789012:role/artos-production-app-service-account"
  },
  "Action": [
    "kms:Decrypt",
    "kms:DescribeKey"
  ],
  "Resource": "*"
}
```

2. **Check ViaService Condition**:
   Ensure KMS permissions include the correct `ViaService` condition for Secrets Manager or S3.

## Related Modules

* [EKS Module](./eks.mdx) - Provides OIDC provider for IAM role authentication
* [S3 Module](./s3.mdx) - S3 buckets that application roles access
* [RDS Module](./rds.mdx) - Database cluster for IAM authentication
* [Bastion Module](./bastion.mdx) - Bastion host with IAM role for EKS management

***

<Note>
  **Module Maintenance**: This module is compatible with Terraform 1.0+ and AWS Provider 5.x. All IAM roles use OIDC authentication via the EKS cluster's identity provider. Ensure the EKS module is deployed first to generate the OIDC provider.
</Note>
