Teaching an Old Dog New Tricks: Forgejo + XMPP

Marius

늙은 개에게 새 재주 가르치기: Forgejo + XMPP

원문은 Marius님이 에 게재했습니다. 이 블로그 구독하기

늙은 개에게 새 재주 가르치기: Forgejo + XMPP

얼마 전 Codeberg를 떠나기로 했다는 결정을 내리고 직접 Forgejo 인스턴스를 운영하기 시작했다. 초기 설정을 마친 뒤 깨달은 건, 직접 호스팅하는 forge는 내가 원하는 대로 만들어 갈 수 있는 forge라는 점이었다. 더 이상 남이 합리적이라고 정해 둔 기능에만 만족할 필요가 없었다. 이제 진짜 내 것으로 만들고, 그동안 Git 호스트가 해주길 바랐던 기능들을 켤 수 있게 됐다.

그중 첫 번째는 가입 과정에 관한 것이다. 지금 Forgejo는 다른 거의 모든 forge와 마찬가지로 이메일 주소를 요구하고 인증 링크를 보내는데, 링크 클릭이 여전히 상대방이 사람이라는 증거로 간주되기 때문인 모양이다. 하지만 나는 사람들이 대신 XMPP JID로 가입할 수 있기를 바란다. 하나는 링크를 받을 수 있는 이메일 주소가 자동화된 스팸에 대한 실질적인 장애물이 된 지 오래라 이메일 인증이 사람들이 생각하는 것만큼 유용하지 않기 때문이고, 다른 하나는 XMPP가 더 우수한 프로토콜이라 애초에 사용자들이 이메일이 아니라 이쪽을 통해 오는 걸 선호하기 때문이다.

아쉽게도 ForgejoXMPP를 네이티브로 지원하지 않는다. 게다가 이 정도 규모의 모놀리스라면 큰 의미가 있을 텐데도, 코어와 거의 독립적으로 확장을 만들 수 있게 해주는 Goshared objects도 활용하지 않는다. 코어 자체를 건드리지 않고는 프로토콜을 깔끔하게 추가할 지점이 없다.

그렇다고 해서 진짜 JID 등록과 인증을 Forgejo 안에 구현하고 싶었던 건 아니다. 그건 풍차에 맞서는 싸움이 되었을 것이다. 경험에서 하는 말이다. 훨씬 가벼운 변경조차 설계 논의사용 사례 수집 같은 관료적인 요구사항에 막혔고, 아마 중간에 어딘가에서 laissez-passer A38까지 요구됐을 테니까. 그래서 제정신이면서도 약간 맛이 간 사람이라면 누구나 할 법한 일을 하기로 했다. Forgejo의 기존 이메일 연동을 XMPP로 브리지하기로 한 것이다.

Switchyard 소개

Switchyard는 한쪽에서는 SMTP를, 다른 쪽에서는 XMPP를 말하는 작은 데몬이다. 이메일과 XMPP는 우연히도 [email protected]라는 동일한 주소 형식을 쓰기 때문에 수신자가 그대로 매핑되고, [email protected]으로 보낸 메시지는 JID [email protected]에 대한 채팅으로 나간다. 서비스가 SMTP로 제출한 메일을 받아 각각을 큐의 작업으로 만들고, 워커가 상시 유지되는 XMPP 연결을 통해 전달한다. 서버가 끊어도 스스로 다시 연결된다.

일반 메일 서버처럼 SMTP를 리스닝하기 때문에, Forgejo는 메일을 넘기기 위해 패치가 필요하지 않다. 실제 메일 서버를 가리킬 때와 같은 블록으로 mailer 엔드포인트로 설정하면 된다:

[mailer]
ENABLED        = true
FROM           = [email protected]
PROTOCOL       = smtp
SMTP_ADDR      = 127.0.0.1
SMTP_PORT      = 587
USER           = forgejo
PASSWD         = `change-me`

반대편에서는 짧은 switchyard.tomlForgejo를 검증할 자격 증명과 제출 포트를 위한 인증서, 그리고 모든 것을 발송할 단일 XMPP 계정을 지정한다:

[redis]
addrs = ["127.0.0.1:6379"]

[smtp]
allowed_ips = ["127.0.0.1/32"]
username = "forgejo"
password = "change-me"

[smtp.tls]
enable = true
cert = "/etc/switchyard/tls/cert.pem"
key = "/etc/switchyard/tls/key.pem"

[xmpp]
server = "xmpp.myhost.tld:5222"
username = "[email protected]"
password = "change-me"

이렇게 해두면 Forgejo가 원래 이메일로 보냈을 모든 메시지, 예를 들어 가입 확인 메일이나 비밀번호 재설정, 새 이슈 알림 등이 대신 이메일 필드에 사용자가 입력한 JIDXMPP를 통해 발송된다. Switchyard는 IDN도 처리한다. 뭐, 알다시피 그게 내 전문이니까. :-)

SMTP는 항상 도메인을 punycode 형태로 전달하는 반면 XMPP는 네이티브 IDN을 사용하므로, 전달 과정에서 수신자 도메인을 디코딩한다. 그래서 Forgejo에서 [email protected]로 가입한 사용자는 XMPP에서 user@マリウス.com로 도달하게 된다.

Forgejo를 넘어서

이 가벼운 서비스는 Forgejo 전용이 아니다. SwitchyardSMTP 서버를 가리킬 수 있는 무엇과도 동작하며, 해당 소프트웨어가 아무것도 바뀌었다는 걸 모른 채 XMPP를 말하게 만든다. 반대쪽에서 해야 할 유일한 조정은 HTML 템플릿의 “Email address” 필드 라벨을 “JID”로 바꾸는 것으로, 사람들이 거기에 무엇을 넣어야 할지 알 수 있게 하는 것이다. Forgejo의 경우 이는 직접 쉽게 유지보수할 수 있는 작은 템플릿 패치다. 업스트림에서 거의 확실히 받아주지 않을 완전한 XMPP 구현을 유지보수하는 것과는 차원이 다른 일이다.

단점은 이메일이 희생된다는 것이다(지금은!). ForgejoSwitchyard와 통신하게 되면, 보내는 모든 것이 XMPP로 가고 이메일 주소를 입력한 사용자는 아무것도 받지 못한다. 나는 이미 이 문제를 해결하는 중이다. Switchyard가 먼저 대상 호스트가 실제로 XMPP 서버인지 확인하고, 아닐 경우 설정해 둔 일반 메일 서버로 메시지를 포워딩하도록 하는 방식이다. 이렇게 하면 하나의 프로토콜(SMTP)이 들어와서, 가능하면 선호하는 프로토콜(XMPP)로 분배되고, 그렇지 않으면 대체 프로토콜(SMTP)로 포워딩된다. 식은 죽 먹기다.

이게 깔끔하고 우아한 엔지니어링인가? 전혀 아니다. SMTP 서버에 XMPP 클라이언트를 연결한 것(중간에 작업 큐 하나 끼고)에 불과하지만, 동작하고 제 역할을 한다. Switchyard 덕분에 나는 업스트림에 있어야 마땅하지만 거의 확실히 절대 올라가지 않을 기능을 얻었고, 내게는 그게 어쩌면 미적 콘테스트에서는 이길지 몰라도 평생 패치로 남을 깔끔한 구현보다 더 중요하다. Forgejo 릴리스마다 계속 머지하고 유지보수해야 할 패치 말이다.

참고: 나는 아직 내 Git 플랫폼인 tty.fail에서 이 구현을 철저히 테스트하는 중이며, 아직 다른 사람들의 가입을 열어두지 않았다. 프로젝트에 기여하고 싶어 미리 이용하고 싶다면, 커뮤니티 채널에 참여해서 알려 주세요!

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글