DigitalOcean이 SMTP를 차단하고 SendGrid 사용을 강제

이 글을 어디에 올릴지 정확히 모르겠지만, 다른 분들도 같은 문제를 겪고 계신지 궁금합니다. DigitalOcean과 Mailgun을 사용하여 공식 설치 가이드를 따랐습니다. 그런데 최근 Jobs::UserEmail 작업 예외가 많이 발생하고 있으며, 테스트 이메일을 보내지 못하고 있습니다.

에러 로그
Message (20584 copies reported)

Job exception: execution expired

Backtrace

/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:663:in `initialize'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:663:in `open'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:663:in `tcp_socket'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:672:in `block in do_start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:185:in `block in timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/timeout-0.4.3/lib/timeout.rb:192:in `timeout'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:671:in `do_start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:642:in `start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.8.1/lib/mail/network/delivery_methods/smtp.rb:109:in `start_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.8.1/lib/mail/network/delivery_methods/smtp.rb:100:in `deliver!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.8.1/lib/mail/message.rb:269:in `deliver!'
/usr/local/lib/ruby/3.3.0/delegate.rb:87:in `method_missing'
/var/www/discourse/lib/email/sender.rb:296:in `send'
/var/www/discourse/app/jobs/regular/user_email.rb:79:in `send_user_email'
/var/www/discourse/app/jobs/regular/user_email.rb:39:in `execute'
/var/www/discourse/app/jobs/base.rb:316:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-6.1.0/lib/rails_multisite/connection_management.rb:21:in `with_connection'
/var/www/discourse/app/jobs/base.rb:303:in `block in perform'
/var/www/discourse/app/jobs/base.rb:299:in `each'
/var/www/discourse/app/jobs/base.rb:299:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:220:in `execute_job'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:185:in `block (4 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:180:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:132:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job/interrupt_handler.rb:9:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:26:in `track'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:134:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:173:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:184:in `block (3 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:145:in `block (6 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:118:in `local'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:144:in `block (5 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:39:in `block in <class:Config>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:139:in `block (4 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:281:in `stats'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:134:in `block (3 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:133:in `block (2 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:85:in `global'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:132:in `block in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:40:in `prepare'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:131:in `dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (2 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `block in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:86:in `process_one'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:76:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:10:in `watchdog'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:19:in `block in safe_thread'

설정 변경이 없었고, 인스턴스는 최신 상태이며, Mailgun 계정은 무료 티어 사용량 범위 내에 있기 때문에 문제의 원인을 파악하지 못했습니다. 따라서 DigitalOcean에서 포트 587을 차단했을 것 같아 지원 티켓을 생성했는데, 결국 SMTP 트래픽에 제한을 두었으며 파트너사인 SendGrid 사용을 권장한다는 짧은 답변을 받았습니다.

DigitalOcean 이메일

계정에 적용된 SMTP 제한에 대한 우려를 이해합니다. DigitalOcean은 전용 이메일 호스트가 아니며 스팸 차단에는 지속적인 노력이 필요합니다. 이에 따라 모든 계정에 제한이 부과되었습니다.

이 문제에 대한 추가적인 배경을 설명드리고자 합니다. 클라우드 환경의 IP 주소는 매우 자주 사용되고 다시 사용 가능한 풀로 반환되므로 동적이고 신뢰할 수 없는 것으로 간주됩니다. 예를 들어, 현재 사용 중인 IP 주소는 책임감 있는 메일 사용자로, 모든 메일 모범 사례를 준수하며 스팸이나 부당한 메일을 보내지 않습니다. 나중에 해당 Droplet이 더 이상 필요하지 않아 삭제하면, 그 IP 주소는 다른 DigitalOcean 사용자에게 할당될 수 있습니다. 그 사용자는 보안 팀이 문제 계정에 조치를 취하기 전에 대량의 스팸을 보내는 기회를 노릴 수 있습니다.

Gmail, Microsoft 등 메일 제공업체는 IP에서 온 메일이 정당한 것인지 아닌지를 판단하기 위해 해당 IP가 나쁜 평판을 얻어야 합니다. 그 시점에는 이미 피해가 발생했을 것입니다. IP 주소가 동적으로 할당되고 본질적으로 위험한 인터넷 서비스 제공업체와 클라우드 호스팅 환경과 같은 플랫폼에서 오는 모든 메일을 차단하는 것이 더 안전합니다.

이 조치는 스팸 공격자가 이용할 수 있는 통로를 줄이는 데 도움이 되지만, 합법적인 사용자에도 영향을 미칩니다. 우리의 악용 방지 운영 팀은 SBL(스팸 블랙리스트)과 협력하여 해당 IP가 목록에서 제거되도록 노력하고 있습니다. 이에 따라 DigitalOcean 플랫폼 전반에서 SMTP 트래픽을 제한하고 있습니다. 이는 계정에 적용된 SMTP 제한을 해제할 수 없음을 의미합니다.

워크플로우에 이메일 관련 필요 사항이 있을 수 있음을 이해합니다. 이 제한에 대한 해결책으로, 우리는 SendGrid와 파트너십을 맺어 IP 평판과 블랙리스트에 대한 걱정 없이 더 나은 솔루션을 모든 고객에게 제공합니다. 이에 대한 자세한 내용은 이곳의 우리 기사에서 확인하실 수 있습니다. SendGrid를 통해 하루 100통의 무료 이메일을 보낼 수 있으며, 무료 티어를 초과하는 요구 사항이 있다면 SendGrid 지원에 문의하여 요구 사항을 충족할 수 있는 더 나은 플랜을 선택하실 수 있습니다.

추가 질문이 있으시면 항상 기꺼이 도와드리겠습니다. 주저하지 마시고 문의해 주시기 바랍니다.

----

이것은 필요한 모든 정보를 수집하여 서비스를 신속하게 처리하기 위한 자동 응답입니다. 추가 도움을 받으려면 이 이메일에 회신해야 합니다.

DigitalOcean Support Team

이런 우발적인 문제를 겪은 다른 분이 계신가요? 정당한 이유 없이 SendGrid로 전환하도록 강요당하고 싶지는 않습니다.

수정…
최근 이 주제 Looks like DO is disabling Smtp in their Discourse hosting plans 를 발견했는데, DigitalOcean을 사용하는 모든 사람이 문제를 겪을 수 있는 것으로 보입니다.

안녕하세요 :waving_hand:

EU 서버에 계신지 확실하지는 않지만, Mailgun에서 현재 연결 문제가 발생하여 이메일을 보내지 못하고 있습니다. /logs에도 오류가 많이 발생하고 있습니다.

확인: https://status.mailgun.com/

감사합니다! 저는 EU 서버를 사용 중이지만, 불행히도 이 문제가 며칠째 지속되고 있으므로 Mailgun의 문제는 아닙니다. 또한, 사용자가 전혀 없으면서도 Mailgun의 EU 서버로 설정된 다른 인스턴스가 있는데, 해당 인스턴스에서는 테스트 이메일을 보내고 있으며 이메일 오류가 전혀 발생하지 않습니다. 따라서 이 문제는 Mailgun의 책임이 아니라고 결론지었습니다.

DigitalOcean만이 전부는 아닙니다. VPS를 유럽의 프로바이더로 이전하는 것을 고려해 볼 수 있습니다.

저도 트랜잭션 이메일 플랫폼을 같은 방식으로 이전했는데, 지금까지 아주 잘 운영되고 있습니다.

Discourse를 설치할 때 DigitalOcean이 기본 선택지라는 것은 알고 있지만, 그들의 VPS 서비스는 특별할 것이 없습니다. 더 이상 적합하지 않다면, 더 이상 적합하지 않은 것입니다.

정말 좋은 조언이고, 감사드립니다. 하지만 DO의 서비스는 제가 현재 구축하고 있고 쉽게 연결하려고 하는 다른 것들과 꽤 잘 어울리거든요. 그래서 그들이 이런 의심스러운 행동을 하지 않았으면 좋겠습니다. 이론적으로 거의 모든 Ubuntu 서버가 문제없이 잘 작동할 수 있으므로, 당신의 지적은 완전히 타당합니다.

UPDATE! 지원 티켓을 열고 충분히 항의하면 포트가 차단 해제됩니다. 테스트 메일이 이제 정상적으로 전송되며 모든 것이 작동하는 것을 확인했습니다.

DO 고객 지원 이메일

안녕하세요,

업데이트를 보내드립니다.

보안 팀이 고객님의 계정에서 SMTP 포트 차단을 해제했습니다.

이제 포트 구성이 가능해야 합니다. 만약 어려움이 있으시다면, 추가 조사가 필요하므로 알려 주십시오!

추가 질문이 있으시면 언제든지 도와드리겠습니다. 주저하지 말고 문의해 주세요.

이 주제에 대해 새로운 업데이트가 있나요? 두 번에 걸쳐 동일한 답변을 받았는데, 정책상 해당 문제를 해결해 줄 수 없다고 하더군요. 그런데 제가 사용하는 이메일 제공업체에서는 2525 포트를 열 수 없습니다. 메인 웹사이트가 그쪽에 있으므로 해당 서비스를 변경하고 싶지는 않습니다.

DO(DigitalOcean)가 Discourse를 가장 많이 호스팅하는 곳인데도 이런 상황이라니, 재고려하지 않는다는 점이 좀 이상하게 느껴집니다. 여러분의 의견은 어떻게 되시나요?

하나만 말씀드릴게요. 필요한 것을 구할 수 없는 곳에 머물면서 왜 돈을 내야 합니까?

글쎄요, 다른 사람에게 효과가 있었다고 해서 포트가 해제된 것 같습니다.

또한, 중요한 점은 다음과 같습니다. 저는 기업 서비스보다 사회적 목적을 가진 협력적 비영리 프로젝트를 지원하려고 합니다. 그래서 최대한 오래 사용할 수 있도록 노력해 보겠습니다.

참고로, DO가 이 문제에 대해 올린 게시글은 다음과 같습니다:

그리고 포트 587(인증 제출)이 기본적으로 차단되어 있는 것 같습니다:

개인적인 의견으로는, 스팸 방지 조치로서 포트 25(SMTP)와 465(SMTPS)를 기본적으로 차단하는 것은 합리적입니다. 하지만 포트 587을 차단하는 것은 무의미하며, 해당 포트의 용도에 대한 무지에서 비롯된 것으로 보입니다.

감사합니다. 저는 DO(디지털 오션)에 열린 티켓을 통해 일부 사용자에게만 영향이 미치는 이유를 설명해 달라고 계속 요청했지만, 그들은 자사의 정책을 고수하고 있습니다. 님이 공유해 주신 링크에서 설명하듯 새 계정과 관련이 있을 것 같습니다. 하지만 여전히 계정의 스팸이 아닌 활동을 검증하거나, 심지어 계정을 감사하는 방법이 있을 수도 있습니다. 그들의 답변은 “플랫폼의 안전을 위해 공개할 수 없는 몇 가지 매개변수가 있습니다” 였습니다. 그래서 저는 그게 다인 것 같습니다. 이메일 서비스를 바꾸거나 discourse용 VPS를 변경해야 할 것 같습니다. 하지만 다른 곳에서도 그런 일이 생길 수 있지 않을까요? 알 수 없네요… :melting_face:

DigitalOcean이 명확하게 설명하지 않은 이유로 3월 6일부터 포트 465와 587을 차단하기 시작했습니다. Release Notes | DigitalOcean Documentation “Droplet에서 SMTP 포트 465와 587이 이제 차단됩니다.” 이 조치는 2년 이상 전에 생성되어 그동안 이메일 전송에 문제가 없었던 Droplet에도 영향을 미쳤습니다.

그러나 저는 DO에서 포트 587을 사용하여 이메일을 전송할 수 있는 Droplet이 있는 반면, 갑자기 이메일 전송이 불가능해진 Droplet도 있습니다.

DO가 어떠한 형태의 통지나 경고 없이 이런 조치를 취한 것에 대해 매우 당황스럽습니다. LON1의 계획된 유지보수 관련 안내를 주당 약 5회나 받는데, 네트워크에 잠재적으로 문제를 일으킬 수 있는 변경 사항에 대해 통지를 하지 못할 이유가 무엇인지 이해가 가지 않습니다. 이 Droplet이 이메일을 보내지 못하고 있다는 사실은 고객이 문제가 있는 것 같다고 연락을 주어서야 알게 되었는데, 이는 부끄러운 일이며 전문성을 떨어뜨리는 결과로 이어졌습니다. 더 이상의 설명은 생략하겠지만, 가능한 한 모든 서버를 DO에서 다른 곳으로 점진적으로 이전할 계획입니다. (요즘 저는 Hetzner를 많이 사용하고 있습니다)

오늘 제가 다소 날카로운 어조의 이메일을 보낸 후, DO는 포트를 해제했고 지금은 모든 것이 정상적으로 작동하고 있습니다.

이메일 전송을 ‘Uptime Monitor’로 감시하는 방법에 대한 조언이 있는 분은 어떻게 생각하시나요? 이메일은 여러 가지 방식으로 실패할 수 있으며, 포럼의 모든 이메일을 모니터링하지 않는 한 이메일이 더 이상 전송되지 않는다는 사실을 항상 ‘알아차리기’는 어렵습니다.