{"id":"IL-0355","org":"Railway","date":"2024-12-17","docs":[],"title":{"en":"Railway's Google Cloud edge stopped routing inbound requests to apps in all regions for about 10 minutes","ko":"Google Cloud 위의 Railway 에지 서버로 들어오는 트래픽이 약 10분 동안 모두 막혔다"},"domain":["networking","gcp"],"impact":{"scope":"Inbound requests through Railway's Google Cloud edge servers to applications; private networking, outbound requests, Metal Hosts and Dedicated Instances were not affected","users":"unknown","region":"All Railway regions on Google Cloud"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://blog.railway.com/p/incident-december-16-2024","kind":"blog","archived":"https://web.archive.org/web/20261023024712/https://blog.railway.com/p/incident-december-16-2024"}],"summary":{"en":"Railway had kept both a static and a dynamic IP block list and over two years moved fully to the dynamic one. A customer reported intermittent timeouts when talking to Railway, and its IP turned out to be on an old static list. Railway removed it by emptying the list's array in Terraform. The Google Cloud Terraform provider treats source_ranges as an optional list, and Terraform, written in Go, does not tell an empty array from nil, so the change removed the source IP list from the GCP firewall rule. A GCP firewall rule without a source range selects all IPs, so the rule meant to block an empty list of IPs blocked everything, and all inbound traffic to Railway's Google Cloud edge was cut. The page reached the current on-call engineer, waking them, rather than the engineer who had merged the change, and the bastion Terraform runner ran a plan before applying the revert, adding about 3 minutes. The revert was applied at 12:58 am UTC and monitoring recovered at 12:59 am.","ko":"Railway 는 2년 동안 정적 차단 목록과 동적 차단 목록을 함께 유지했고, DDoS 대응이 나아지면서 동적 방식으로 완전히 옮겼다. 한 고객이 자기 애플리케이션이 Railway 와 통신할 때 간헐적으로 타임아웃이 난다고 알려 왔고, 조사해 보니 그 고객의 IP 가 동적 방식 이전 시절의 정적 목록에 막혀 있었다. Railway 는 이 IP 목록의 배열을 비워 차단 목록에서 빼기로 했다.\n\nGoogle Cloud Terraform Provider 는 source_ranges 를 선택 사항인 목록으로 받고, 아무 값도 없으면 규칙의 동작을 모든 대상에 적용한다. Golang 으로 만든 Terraform 은 빈 배열과 nil 을 구분하지 않는다. GCP 방화벽 규칙은 출발지 IP 범위를 정하지 않으면 모든 IP 를 고른다. 그래서 빈 목록을 받은 Terraform 이 방화벽 규칙의 출발지 IP 목록을 지웠고, Terraform 에서 \"이 빈 IP 목록을 막는다\" 로 읽히던 규칙이 실제로는 \"모든 것을 막는다\" 가 됐다.\n\n2024-12-17 UTC 오전 12시 47분에 변경이 머지됐고, 12시 49분에 자동 알림이 엔지니어에게 갔다. Railway 는 먼저 되돌리고 원인은 나중에 찾는다는 정책에 따라 12시 54분에 변경을 되돌렸고, 12시 58분에 적용했으며, 12시 59분에 자동 모니터링이 정상으로 돌아왔다. 알림은 변경을 머지한 엔지니어 대신 온콜 당번을 깨웠다. 또 bastion 의 Terraform 실행기가 apply 전에 plan 을 먼저 실행해 복구가 3분 늦어졌다."},"trigger":{"en":"A merged Terraform change emptied the array of a static IP block list, to unblock a customer whose IP had been blocked since before Railway moved to a dynamic block list and who had reported intermittent timeouts.","ko":"Railway 엔지니어가 오래된 정적 차단 IP 목록을 비우려고 배열을 빈 값으로 바꾼 Terraform 변경을 머지했다. Google Cloud 방화벽 규칙에서 출발지 IP 범위가 사라져 규칙이 모든 IP 를 막게 됐다."},"severity":"unknown","symptoms":["service-unavailable"],"timeline":{"detected_after":"2m","resolved_after":"12m"},"date_kind":"incident","detection":{"en":"Automated alerting paged the current on-call engineer at 12:49 am UTC, two minutes after the change was merged.","ko":"변경을 머지하고 2분 뒤인 UTC 오전 12시 49분에 자동 알림이 Railway 엔지니어에게 갔다. 이 알림은 변경을 머지한 엔지니어가 아니라 온콜 당번을 깨웠다."},"components":["terraform","vpc-routing-and-firewall"],"root_cause":"config-change","remediation":[{"text":{"en":"Reverted the change to the IP allow-list rules","ko":"먼저 되돌리고 원인은 나중에 찾는다는 정책에 따라 UTC 오전 12시 54분에 변경을 되돌렸다."}},{"text":{"en":"Applied the reverted change through the Terraform runner","ko":"되돌린 변경을 UTC 오전 12시 58분에 적용했고, 12시 59분에 자동 모니터링이 정상으로 돌아왔다."}}],"action_items":[{"text":{"en":"Automatically apply a page override for merged changes, for faster notification","ko":"알림이 더 빨리 닿도록 변경을 머지할 때 \"page override\" 를 자동으로 건다."}},{"text":{"en":"Skip the Terraform plan phase during a revert","ko":"Terraform 으로 되돌릴 때는 plan 단계를 건너뛴다."}},{"text":{"en":"Remove the offending firewall rule safely","ko":"문제가 된 방화벽 규칙을 안전하게 지운다."}},{"text":{"en":"Audit all remaining legacy Google Cloud firewall rules for loose specificity","ko":"남은 옛 Google Cloud 방화벽 규칙 가운데 범위가 느슨하게 정해진 것이 있는지 모두 감사한다."}},{"text":{"en":"Move to its own metal infrastructure while keeping GCP and adding AWS, for resilience and fault isolation","ko":"GCP 지원을 유지하면서 AWS 를 더하고 리전마다 여러 metal 데이터 센터에 투자해, 여러 종류의 컴퓨트를 지원하는 복원력 있는 인프라를 만든다. AWS 는 지금 일부 고객과 시험하고 있다."}}],"contributing":["assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"Railway's edge servers on Google Cloud could not route inbound requests to applications in any region for about 10 minutes, on 17 December 2024 UTC. Private networking, outbound requests, Metal Hosts and Dedicated Instances kept working.\n\nAutomated alerting notified a Railway engineer at 12:49 am UTC, two minutes after a change to IP allow-list rules was merged at 12:47 am, and automated monitoring recovered at 12:59 am after the change was reverted and applied.","ko":"Google Cloud 에 있는 Railway 에지 서버가 들어오는 요청을 애플리케이션으로 보내지 못했다. 모든 리전에서 Google Cloud 로 들어오는 트래픽이 약 10분 동안 끊겼다.\n\n사설 네트워크, 나가는 요청, Metal Hosts, Dedicated Instances 는 영향을 받지 않았다.\n\n변경을 머지하고 2분 뒤 자동 알림이 울렸다. 이 알림은 변경을 머지한 엔지니어가 아니라 온콜 당번을 깨웠다."},"updated_at":"2026-11-14T12:34:00.924192Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0355","org_slug":"railway"}