
dushed는 메모, 북마크, 파일, 피드, 킨들 하이라이트와 플래시카드를 한곳에 연결하는 지식 관리 웹 앱이다. 개발팀은 인증서를 별도 도구로 갱신한 뒤 서버를 재시작하는 과정 없이, 실행 중 인증서를 발급하고 갱신하는 방식을 구현했다.
이 블로그 글은 AI가 작성하지 않았다.
일반적으로 웹 서버의 TLS 인증서는 Certbot이나 다른 ACME 클라이언트를 이용해 별도 절차로 발급받는다. 하지만 dushed 개발팀은 인증서 갱신을 위한 별도 절차를 유지하고, 갱신 후 dushed 프로세스를 재시작해야 하는 상황을 피하고자 했다.
Go로 작성된 웹 앱에서 HTTPS 서버를 설정하는 기본 방식은 다음과 같다.
func hello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello world. Welcome to my website"))
}
srv := &http.Server{
Addr: "127.0.0.1:8080",
Handler: http.HandlerFunc(hello),
}
err := srv.ListenAndServeTLS("/path/to/cert", "/path/to/key")
이 방식은 지정한 경로에 TLS 인증서와 키가 이미 있어야 한다. 대신 HTTP 서버의 선택 항목인 TLSConfig를 사용하면 서버의 TLS 설정을 지정할 수 있다.
tls.Config에는 클라이언트의 ClientHello를 바탕으로 TLS 인증서를 반환하는 함수인 GetCertificate 필드가 있다. 이 함수 안에서 인증서를 발급하거나 갱신할 수 있다.
tlsConf := &tls.Config{
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
crt, err := getCert(info)
if err != nil {return nil, err}
return crt, nil
},
}
srv := &http.Server{
Addr: "127.0.0.1:8080",
Handler: http.HandlerFunc(hello),
TLSConfig: tlsConf,
}
// Use empty string arguments so that srv.TLSConfig is used.
err := srv.ListenAndServeTLS("", "")
인증서 처리의 대부분은 getCert 함수에 들어간다. 이 함수에서 ACME 프로토콜 API를 호출할 수 있다. 인증서 캐싱, SNI에 따라 서로 다른 인증서 제공, 만료 전 선제 갱신도 구현할 수 있다. dushed는 자체 ACME 클라이언트를 작성하는 대신 golang.org/x/crypto/acme/autocert를 사용했다.
dushed는 이 방식으로 실행 중 TLS 인증서를 발급하고 갱신한다. 개발팀은 이 방식이 무중단 배포에서도 작동한다고 설명한다. ListenConfig로 SO_REUSEPORT와 SO_REUSEADDR를 설정해 이전 프로세스와 새 프로세스가 동시에 연결을 수신하도록 한다. getCert에서 Let’s Encrypt로부터 발급받은 인증서는 디스크와 메모리에 저장돼 배포 중 두 프로세스 모두에서 사용할 수 있다. 관련 문서는 ListenConfig에서 확인할 수 있다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요