ES /docs

batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/f08cf04288c144f38

RCA: batch_pull! AccessDenied on ecs:DescribeTasks

Overview#

What Happened#

2026-07-13 19:02 KST 부터 cupixworks-migration-worker (tesla EB migrationworker role) 의 AwsTask.batch_pull! cron job 이 ecs:DescribeTasks API 호출에서 AWS AccessDenied 를 반환하면서 74분간 988건의 에러를 발생시켰다. tesla-api-production-5nod IAM user 의 identity-based policy 가 새 ECS long ARN 포맷(task/<cluster>/<taskId>)의 task 리소스와 매칭되지 않아 ecs:DescribeTasks 권한이 거부되었다.

Quick Facts#

Field Value
exception.class AwsTask (message-only, exception class not captured)
exception.message User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action
top_frame app/models/aws_task.rb:87
env production, region tag ap-southeast-1, ECS cluster in us-west-2
iam_user arn:aws:iam::002596530511:user/system/tesla-api-production-5nod
ecs_cluster cupix-tesla-ece (account 002596530511, region us-west-2)

Affected Teams#

Team / Domain Error Count Impact
cupixworks-migration-worker (tesla EB migrationworker) 988 ECS task status polling 중단 — AwsTask 레코드의 last_status/desired_status 가 갱신되지 않아 downstream job callback 및 stopped 처리 지연

Timeline#

  1. 2026-07-13 19:02 KSTAwsTask.batch_pull!AccessDenied 발생 (first_seen)
  2. 2026-07-13 19:02 ~ 20:16 KST — 988건 반복 (cron 이 배치별로 계속 재시도)
  3. 2026-07-13 20:16 KST — 클러스터 마지막 관측 (last_seen)

Error Log#

Datadog Logs

text
batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/f08cf04288c144f384c0a517fc3ec322,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/522564c7ebb14f7a8ce1ba152e821400,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/ed4c0e4d621e47e89a0d92f933a76d10 - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action

Impact#

  • Service: cupixworks-migration-worker
  • 발생 횟수: 988
  • 최초 발생: 2026-07-13 19:02 KST
  • 최근 발생: 2026-07-13 20:16 KST

batch_pull! 은 4개 스코프(blank/pending/running/stopping) 에 대해 주기적으로 실행되어 ECS task 의 최신 상태를 DB (AwsTask) 에 반영한다. 이 호출이 실패하면:

  • AwsTask 레코드의 last_status 가 stale 하게 남는다.
  • PRE_RUNNING_STATUSES (PROVISIONING, PENDING, ACTIVATING) 에서 실제로는 RUNNING/STOPPED 로 전이된 task 들이 DB 상 갱신되지 않음.
  • after_save 에 걸린 job.run_task_stopped_callbacks(task_id) 가 트리거되지 않아 상위 job 파이프라인의 완료 처리가 지연될 수 있다.

Root Cause Summary#

tesla-api-production-5nod IAM user 에 연결된 api_ecs_policy (Terraform: cupix-service/api-eb/main.tf:769-801) 는 ecs:* 액션을 arn:aws:ecs:*:*:task/* 등의 resource-scoped ARN 목록으로 제한하고 있다. AWS ECS 는 2020년 이후 task ARN 포맷을 old (task/<taskId>) 에서 long (task/<clusterName>/<taskId>) 으로 전환했고, 실제 호출되는 ARN 은 arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/<taskId> (long 포맷) 이다. IAM ARN wildcard */ 를 매칭하지 않으므로 task/* 패턴은 long ARN 을 매칭하지 못하고, ecs:DescribeTasks 호출이 어느 Statement 에도 인가되지 않아 AWS 가 AccessDenied 로 응답한다. 에러 메시지의 on resource: * 는 매칭되는 정책이 하나도 없을 때 나타나는 AWS 의 표준 응답 문구다. 이 문제가 이번 시점에 대량 발생한 이유는 로그만으로는 특정할 수 없으며(코드/Terraform 커밋 이력 상 관련 변경 없음, 최근 6bfe7d3f/030403df 은 무관), 계정 단위 ECS long ARN 강제 전환·SCP·최근 배포된 새 task 들이 처음으로 long-format ARN 을 저장하기 시작한 데 따른 것 중 하나로 추정된다 (uncertain -- needs verification against AWS account settings and recent deploys).

Technical Analysis#

Code Path#

Cron entry → Cupix::Cron::AwsTask.pull_*AwsTask.batch_pull!(tasks)Aws::ECS::Client#describe_tasksAccessDeniedrescue StandardError 로그.

  • Entry point: lib/cupix/cron/aws_task.rb:3-31
  • Failure point: app/models/aws_task.rb:55-59 (describe_tasks call), app/models/aws_task.rb:86-88 (rescue/log)

Cron entry:

lib/cupix/cron/aws_task.rb:3-31ruby
def self.pull_blank
  tasks = ::AwsTask.blank.to_a
  ::AwsTask.batch_pull!(tasks)
  # ...
end

def self.pull_pending
  tasks = ::AwsTask.pending.to_a
  ::AwsTask.batch_pull!(tasks)
  # ...
end

def self.pull_all
  tasks = ::AwsTask.pullable.to_a
  ::AwsTask.batch_pull!(tasks)
  # ...
end

배치 호출 지점:

app/models/aws_task.rb:45-88ruby
def self.batch_pull!(tasks)
  tasks = tasks.to_a
  return if tasks.empty?

  ecs = tasks.first.ecs_client
  cluster_name = $AWS[:ecs][:cluster_name]
  task_ids = tasks.filter_map(&:task_id)
  tasks_by_task_id = tasks.index_by(&:task_id)

  task_ids.each_slice(BATCH_SIZE) do |batch_ids|
    resp = ecs.describe_tasks({
      tasks: batch_ids,
      cluster: cluster_name,
      include: ['TAGS']
    })
    # ...
  rescue StandardError => e
    Cupix::Logger.error("batch_pull! error on batch #{batch_ids.first(3).join(',')} - #{e.message}", class: name, function: __method__)
  end
end

task_id 필드에 저장된 값은 full ARN 형식이며, describe_tasks 는 이를 그대로 AWS 에 전달한다. AWS 는 호출자 IAM user 의 policy 를 조회해 각 task ARN 에 대해 매칭되는 Statement 가 있는지 확인한 뒤, 없으면 위 에러를 던진다.

문제의 IAM policy (실제 정책은 AWS 계정에 있으나 Terraform 정의는 아래와 같음):

cupix-infrastructure/cupix-service/api-eb/main.tf:769-801hcl
resource "aws_iam_policy" "api_ecs_policy" {
  # ...
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "HandleECS",
        Effect = "Allow",
        Action = [
          "ecs:*"
        ],
        Resource = [
          local.resource_ecs,                           # cluster/<name>
          "arn:aws:ecs:*:*:task/*",                     # 새 long-format task ARN 미매칭
          "arn:aws:ecs:*:*:container-instance/*",
          "arn:aws:ecs:*:*:service/*",
          "arn:aws:ecs:*:*:task-set/*/*/*",
          "arn:aws:ecs:*:*:task-definition/*:*"
        ]
      }
    ]
  })
}

기대 동작: ecs:DescribeTasksarn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/<taskId> 리소스에 대해 허용되어야 함.

실제 동작: task/*task/<taskId> (segment 1개) 만 매칭하고 task/<cluster>/<taskId> (segment 2개) 를 매칭하지 못하므로, describe_tasks 호출이 모든 대상 task 에 대해 거부됨. AWS 는 매칭되는 Statement 가 없을 때 on resource: * 를 반환한다.

Log Evidence#

Datadog query:

text
service:cupixworks-migration-worker status:error "batch_pull!"

Sample logs (최근 3시간, 5건 확인):

json
{
  "timestamp": "2026-07-13 20:16:23 KST",
  "status": "error",
  "class": "AwsTask",
  "function": "batch_pull!",
  "message": "batch_pull! error on batch arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/48e9afa19f0a4682a1d662f21d4eec79,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/d1b13a496a7848e09c8d1ad0d1c85a34,arn:aws:ecs:us-west-2:002596530511:task/cupix-tesla-ece/3454fc8384d64a86834a75295c23a3a2 - User: arn:aws:iam::002596530511:user/system/tesla-api-production-5nod is not authorized to perform: ecs:DescribeTasks on resource: * because no identity-based policy allows the ecs:DescribeTasks action"
}
  • 관측된 모든 task ARN 은 long-format (task/cupix-tesla-ece/<hex>) 이다.
  • 호출자는 IAM user tesla-api-production-5nodcupix-infrastructure/cupix-service/api-eb/main.tf:642-647aws_iam_user "api_user" (name = tesla-api-${var.service_env}-${random_string}) 로 생성되는 EB API 인스턴스용 access key 다.
  • 상태 보드상 동일 서비스에서 2026-07-10, 2026-07-13 04:07 UTC 에도 root_cause_types=unknown 로 분류된 별개 클러스터가 있었으나 fingerprint 가 달라 직접 상관관계는 확인되지 않는다 (uncertain -- needs verification).

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 IAM policy api_ecs_policytask/* resource pattern 이 long ARN (task/<cluster>/<taskId>) 을 매칭하지 못해 ecs:DescribeTasks 가 거부 Terraform cupix-infrastructure/cupix-service/api-eb/main.tf:791-794arn:aws:ecs:*:*:task/*; 실제 호출 ARN 은 task/cupix-tesla-ece/<taskId> (long); AWS IAM wildcard */ 미매칭; 에러 문구 no identity-based policy allows the ecs:DescribeTasks action Confirmed
H2 특정 리전(ap-southeast-1) 에서 ECS 엔드포인트가 다르게 동작 클러스터 tag regions: [ap-southeast-1] 실제 호출 ARN 및 클러스터는 us-west-2 (cupix-tesla-ece); AwsTask#ecs_clientENV['CUPIX_REGION'] || 'us-west-2' 로 고정 (app/models/aws_task.rb:91-93) Rejected
H3 코드 변경으로 인증되지 않은 새 API 호출이 추가됨 git log -L 769,801:cupix-service/api-eb/main.tf 은 whitespace-only 변경(030403df); AwsTask.batch_pull! 및 cron 코드 최근 변경 없음 Rejected
H4 Aws 자격 증명이 만료/rotate 되어 다른 user 로 전환 에러 메시지는 AccessDenied (인증 성공 후 인가 실패) 이지 InvalidClientTokenId/SignatureDoesNotMatch 가 아님; user ARN 도 정상 tesla-api-production-5nod 로 표시 Rejected
H5 Trigger 는 계정/리전 단위 ECS long-ARN 강제 활성화 또는 SCP 변경 그 이전에는 same policy 로 정상 동작; 문제가 특정 시각(19:02 KST)부터 대량 발생; long-format ARN 이 정책 매칭을 실패시키는 것과 일관 AWS 계정 설정/CloudTrail 미조회 — 로그만으로는 미확정 Inconclusive (uncertain -- needs verification via CloudTrail PutAccountSetting / SCP 변경 이력)

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: cupix-infrastructure/cupix-service/api-eb/main.tf:791-798 (api_ecs_policyHandleECS statement 의 Resource 배열)
  • 접근: long-format task ARN 을 매칭하도록 wildcard 패턴을 넓히고, list/describe 계열 API 는 resource-level 제한이 아예 불가능한 경우가 있으므로 이를 분리한다.
    • arn:aws:ecs:*:*:task/*arn:aws:ecs:*:*:task/*/* 로 변경 (또는 두 패턴을 병기)
    • 관련 리소스인 container-instance/*, service/* 도 필요 시 */* 로 확장 검토 (AWS long ARN 정책 동일하게 적용됨)
    • 근거: AWS 문서 Amazon ECS resource IDs 에 따르면 long ARN 은 cluster segment 를 포함하므로 policy resource 도 task/*/* 패턴이 필요.
  • 선행 확인: AWS 콘솔의 ECS → Account Settings 에서 이 계정의 taskLongArnFormatenabled 인지, 언제 전환됐는지 CloudTrail (PutAccountSetting) 로 확인 — H5 검증 및 다른 계정 대비 rollout 순서 판단에 필요.
  • rollback 안전성: 정책을 더 넓게 만들 뿐 축소가 없으므로 rollback 은 이 커밋을 revert 하면 즉시 이전 상태로 돌아간다.

단기 개선 (1주 이내)#

  • AwsTask.batch_pull!rescue StandardError 를 세분화: Aws::ECS::Errors::AccessDeniedExceptionerror 로 유지하되, transient network/ThrottlingExceptionwarn 로 다운그레이드하고, 반복 발생 시 반복 로그가 스팸이 되지 않도록 batch 단위로 dedup 한다 (app/models/aws_task.rb:86-88). 근거: 이번 사고는 4분 창에 988건이 로그를 도배해 alert 노이즈가 심했다.
  • describe_tasks 결과가 지속 실패할 때 AwsTask 스코프(blank/pending/running/stopping) 별 처리 지연을 감지할 수 있는 최소 metric (성공/실패 카운트) 을 추가한다.

장기 개선 (재발 방지)#

  • IAM policy 의 ecs:* action 을 그대로 두는 대신, migration/worker 가 실제로 사용하는 action 집합만 나열하고 resource wildcard 대신 Condition: { StringEquals: { "ecs:cluster": "arn:aws:ecs:...:cluster/cupix-tesla-ece" } } 로 좁히는 것을 고려. DescribeTasks, StopTask 등은 이 조건 키를 지원한다. 이렇게 하면 ARN 포맷 변화에 대한 취약성이 사라진다.
  • Terraform 정책 변경에 대한 awspec/iamlive 기반의 규칙 검증(예: long ARN 매칭 여부) 을 CI 에서 실행.
  • 계정-레벨 ARN 포맷 전환 이벤트를 Datadog monitor 로 잡을 수 있도록 CloudTrail PutAccountSetting 을 forwarding.

Monitoring#

Datadog 쿼리 (release dashboard timeseries widget 용, writing-datadog-monitoring-queries 가이드에 따라 aggregation 함수만 사용):

  • batch_pull! AccessDenied 재발 감지:
text
service:cupixworks-migration-worker status:error @class:AwsTask @function:batch_pull!
  • ECS 인가 실패 전반 (다른 코드 경로 포함):
text
service:cupixworks-migration-worker status:error "not authorized to perform: ecs:"
  • 성공 카운트 (fetched 로그) — 실패 대비 정상성 확인:
text
service:cupixworks-migration-worker status:info @class:AwsTask "Task fetched"

메트릭 알림 후보:

  • 5분 rolling window 에서 batch_pull! error rate > 10/min 시 P2 알림.
  • Task fetched info 로그가 15분간 0 건이면 cron 이 완전히 멈춘 것으로 P1 알림.

Risk Assessment#

  • Risk level: medium — IAM policy 확장은 blast radius 가 큰 리소스이므로 반드시 staging → production 순으로 배포하고 CloudTrail Deny 이벤트를 배포 후 30분간 모니터링해야 한다. 다만 이번 변경은 wildcard 확장(축소 아님)이라 기존 기능을 잃을 위험은 없다.
  • 예상 복잡도: standard — Terraform 1줄 수정 + plan/apply, 이후 dev/stage/production 순차 적용. 코드 변경은 최소.