У меня есть приложение на Go, в котором в одном из потоков работает простой HTTP сервер. К этому серверу обращаются по HTTPS. Запрос приходит на Pound - HTTP/HTTPS reverse-proxy and load-balancer и перенаправляется в приложение. TLS сертификат изготавливается и обновляется с помощью Let's Encrypt.
Простая и привычная схема. Правда, чаще в этой схеме бывает Nginx, но в этой статье мы не будем рассуждать, почему Pound, а не Nginx. Все очень хорошо, но меня последнее время начинает раздражать, когда к простому и понятному коду на Go нужно прикрутить небольшого динозаврика с пять-шестью скриптами на Ansible, закатать все это в деплой и радоваться тому, как это все славно улеглось в небольшой виртуалке.
Я где-то там, в своем начале длительное время писал на Assembler-е. Размер откомпилированной программы больше одного Кб считался огромным. Видимо с тех пор во мне застряло желание экономить ресурсы компьютера, несмотря на то, что разработчики железа предоставляют их все больше и больше. Так же я ни как не могу привыкнуть к этим жутким папкам под названием ‘node_modules’ и всегда хочу стереть их содержимое.
И я задумался...
Задумался над тем, как можно сократить количество участников в задаче «HTTP-сервер с сертификатом Let's Encrypt на Go». Год думал. Шучу. Мое приложение с динозавриками замечательно проработало год, и настало время сделать обновления.
И решение нашлось. Оказывается, все, что нужно для облегчения этой задачи, есть в библиотеке Go. Итак, вот что нужно написать в программе на Go для того, что бы работал HTTPS сервер и автоматически создавался и обновлялся TLS сертификат по технологии Let’s Encrypt:
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, TLS user! Your config: %+v", r.TLS)
})
log.Fatal(http.Serve(autocert.NewListener(domain), mux))
И все! Да, и все - те же самые стандартные три строчки, которые присутствуют во всех примерах создания HTTP-сервера. Ищем отличия, а я посмотрю на секундомер...
Ну конечно! - ‘autocert.NewListener(domain)’ в ‘http.Serve’.
Приложение, использующее этот код, должно быть запущено с правами администратора, и на хосте должен быть белый IP адрес. Без него Let’s Encrypt не сможет создать сертификат.
Для того что бы работала привычная нам переадресация с HTTP на HTTPS, добавим еще пару строк кода перед предыдущим кодом:
go func() {
if err := http.ListenAndServe(":80", http.HandlerFunc(redirectTLS)); err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
Здесь мы создали еще один HTTP сервер, работающий на 80-м порту и пересылающий все на наш основной сервер с помощью функции ‘redirectTLS’:
func redirectTLS(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://"+domain+":443"+r.RequestURI, http.StatusMovedPermanently)
}
Приложение имеет параметр '–domain' и строка запуска выглядит так:
sudo go run . -domain=example.com
Конечно. Для запуска этого приложения у вас должен быть зарегистрированный домен и белый IP адрес.
Программа размещена на Github:
https://github.com/kirill-scherba/https-example
Вот ещё один адрес на Gitflic, пока он запасной:
https://gitflic.ru/project/kirill-scherba/https-example
vebeer
А права администратора нужны, чтобы слушать на 80м и 443м порту или для чего-то другого? А можно же кастомные порты указать для этого, 8080 и 8443, например? Хотя, кажется, что тогда LE не сможет прийти на 80й порт и верифицировать домен
kirill-scherba Автор
Да, вы все правильно себе ответили, тогда Let's Encrypt не сможет получить или обновить сертификат.
A в случае когда мы используем для этого Pound или Nginx , то в момент получения (или обновления) сертификата Pound/Nginx нужно выключать по той же причине - Let's Encrypt не сможет прийти на 80ый порт и верифицировать домен.