Least Privilege #
Least privilege adalah prinsip keamanan yang sederhana: berikan akses hanya sebesar yang dibutuhkan, tidak lebih. Di konteks Terraform, ini berarti IAM role yang digunakan untuk menjalankan Terraform tidak perlu AdministratorAccess hanya karena lebih mudah dikonfigurasi. Permission berlebihan adalah risiko yang nyata — jika credentials Terraform dikompromikan, penyerang mendapat akses seluas permission yang diberikan. Merancang permission yang minimal tapi cukup untuk Terraform adalah investasi yang jauh lebih berharga dari yang terlihat.
Mengapa AdministratorAccess Berbahaya #
RISIKO ADMINISTRATOR ACCESS:
Jika TerraformCIRole punya AdministratorAccess:
- Dikompromikan → penyerang bisa hapus semua resource
- Dikompromikan → penyerang bisa buat resource baru (crypto mining, dll.)
- Dikompromikan → penyerang bisa buat IAM user baru dengan akses penuh
- Developer yang salah ketik destroy → seluruh infrastruktur hilang
- Bug di pipeline → bisa mempengaruhi resource di luar scope yang dikelola
Dengan least privilege:
- Dikompromikan → penyerang hanya bisa akses apa yang Terraform butuhkan
- Developer salah ketik → hanya resource yang Terraform kelola yang terpengaruh
- Bug di pipeline → blast radius terbatas pada scope konfigurasi
Strategi: Permission Berbeda untuk Plan dan Apply #
Plan hanya membaca — tidak butuh write access. Apply butuh write. Memberikan permission yang sama untuk keduanya adalah pemborosan privilege yang tidak perlu.
// IAM Policy untuk TerraformPlanRole (digunakan saat plan)
// Hanya read access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"rds:Describe*",
"s3:GetObject",
"s3:ListBucket",
"iam:Get*",
"iam:List*",
"sts:GetCallerIdentity"
],
"Resource": "*"
}
]
}
// IAM Policy untuk TerraformApplyRole (digunakan saat apply)
// Write access hanya untuk resource yang dikelola Terraform
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:*",
"rds:*",
"s3:*",
"elasticloadbalancing:*"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-southeast-1"
}
}
},
{
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:DeleteRole",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:DeleteRolePolicy",
"iam:PassRole"
],
"Resource": "arn:aws:iam::*:role/app-*"
// Hanya role dengan prefix "app-" yang bisa dibuat/diubah
}
]
}
Membatasi Scope dengan Condition dan Resource ARN #
// Batasi lebih ketat dengan Resource ARN dan Condition
{
"Version": "2012-10-17",
"Statement": [
// EC2: hanya bisa kelola instance yang dibuat Terraform (dengan tag ManagedBy)
{
"Effect": "Allow",
"Action": ["ec2:TerminateInstances", "ec2:StopInstances"],
"Resource": "arn:aws:ec2:ap-southeast-1:123456789:instance/*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/ManagedBy": "terraform"
}
}
},
// S3: hanya bisa modifikasi bucket tertentu (bukan semua bucket)
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::production-app-*",
"arn:aws:s3:::production-app-*/*"
]
},
// Cegah privilege escalation: Terraform tidak bisa buat user dengan akses lebih besar
{
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:AttachUserPolicy",
"iam:PutUserPolicy"
],
"Resource": "*"
}
]
}
Menggunakan IAM Access Analyzer untuk Temukan Permission yang Dibutuhkan #
Alih-alih menebak permission apa yang dibutuhkan, gunakan IAM Access Analyzer untuk menganalisis CloudTrail log dan menghasilkan policy yang tepat.
# 1. Jalankan Terraform dengan AdministratorAccess sementara
# sambil CloudTrail merekam semua API call
# 2. Generate policy dari CloudTrail menggunakan IAM Access Analyzer
aws iam generate-policy --cloudtrail-arn arn:aws:cloudtrail:... \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-02T00:00:00Z \
--iam-entity-arn arn:aws:iam::123456789:role/TerraformTestRole
# 3. Review policy yang di-generate — IAM Access Analyzer menampilkan
# hanya permission yang benar-benar digunakan
# 4. Terapkan policy yang lebih ketat, test lagi
# 5. Iterasi sampai Terraform bisa berjalan dengan permission minimal
Permission untuk Tim: RBAC yang Tepat #
Tidak semua orang di tim perlu permission yang sama untuk berinteraksi dengan Terraform.
MATRIX PERMISSION TIM:
Developer (engineer biasa):
- Baca konfigurasi .tf di repository ✓
- Baca plan output di PR ✓
- Tidak bisa apply ✗
- Tidak bisa akses state langsung ✗
- AWS access: read-only di development, tidak ada di production
Senior Engineer / Tech Lead:
- Semua yang developer bisa ✓
- Bisa review dan approve PR ✓
- Bisa trigger apply di staging ✓
- Tidak bisa apply langsung di production ✗ (harus lewat pipeline)
Infrastructure / DevOps Engineer:
- Semua yang senior engineer bisa ✓
- Bisa apply di production melalui pipeline ✓
- Bisa akses state (untuk debugging) ✓
- Bisa modifikasi pipeline configuration ✓
On-call / SRE (emergency access):
- Temporary elevated access saat insiden
- Semua aksi diaudit dan di-review post-insiden
- Akses expire otomatis setelah N jam
CI/CD Pipeline:
- Plan: read-only AWS access ✓
- Apply: write access terbatas pada resource yang dikelola ✓
- Tidak bisa modify IAM roles atau pipeline configuration ✗
# Konfigurasi IAM group dan permission via Terraform
resource "aws_iam_group" "terraform_readers" {
name = "terraform-readers"
}
resource "aws_iam_group_policy_attachment" "terraform_readers" {
group = aws_iam_group.terraform_readers.name
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
# Developer: hanya read access ke development account
resource "aws_iam_group" "developers" {
name = "developers"
}
resource "aws_iam_group_policy" "developers_dev_access" {
name = "dev-access"
group = aws_iam_group.developers.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["sts:AssumeRole"]
Resource = "arn:aws:iam::${var.dev_account_id}:role/DeveloperRole"
# Hanya bisa assume role di dev account, tidak di production
}]
})
}
Monitoring dan Alert untuk Permission Violation #
# CloudWatch alarm untuk mendeteksi upaya akses yang tidak authorized
resource "aws_cloudwatch_metric_alarm" "unauthorized_api_calls" {
alarm_name = "terraform-unauthorized-api-calls"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "1"
metric_name = "ErrorCount"
namespace = "CloudTrailMetrics"
period = "300"
statistic = "Sum"
threshold = "1"
alarm_description = "Alert saat ada API call yang di-deny (unauthorized)"
alarm_actions = [aws_sns_topic.security_alerts.arn]
}
# CloudWatch Logs metric filter untuk AccessDenied
resource "aws_cloudwatch_log_metric_filter" "unauthorized_api" {
name = "unauthorized-api-calls"
log_group_name = aws_cloudwatch_log_group.cloudtrail.name
pattern = "{ ($.errorCode = \"*UnauthorizedAccess*\") || ($.errorCode = \"AccessDenied\") }"
metric_transformation {
name = "ErrorCount"
namespace = "CloudTrailMetrics"
value = "1"
}
}
Ringkasan #
AdministratorAccessbukan solusi yang “aman karena lebih mudah” — ini adalah risiko yang nyata jika credentials dikompromikan atau ada kesalahan operasional.- Pisahkan permission plan dan apply — plan hanya butuh read access, apply butuh write. IAM role berbeda untuk setiap konteks.
- Batasi scope dengan Resource ARN dan Condition —
ec2:TerminateInstancesyang dibatasi hanya pada instance berisi tagManagedBy=terraformjauh lebih aman dariec2:*.- IAM Access Analyzer bisa menganalisis CloudTrail log untuk menghasilkan policy minimal yang mencakup semua API call yang benar-benar digunakan Terraform.
- RBAC yang tepat — developer cukup read-only, engineer senior bisa apply staging, hanya infrastructure engineer yang bisa apply production melalui pipeline.
- Monitor unauthorized access — CloudWatch alarm pada
AccessDeniedmemberikan sinyal awal jika ada yang mencoba akses di luar scope yang diizinkan.
← Sebelumnya: Secret Exposure Risk Berikutnya: Common Security Mistake →