Commit 031b7f10 authored by Lukáš Lalinský's avatar Lukáš Lalinský

Add support for alternative domains

parent f4c9a733
Pipeline #20262 passed with stage
in 32 seconds
......@@ -17,12 +17,17 @@ const certbotWebRootDir = "/tmp/letsencrypt/"
const certbotEmailEnvName = "LETSENCRYPT_EMAIL"
const certbotDryRunEnvName = "LETSENCRYPT_DRY_RUN"
type newCertRequest struct {
domain string
altDomains []string
}
// LetsEncryptServer represents a Let's Encrypt validation master server
type LetsEncryptServer struct {
email string
dryRun bool
webroot string
newCertChannel chan string
newCertChannel chan newCertRequest
lastModified time.Time
}
......@@ -34,7 +39,7 @@ func lastModifiedNow() time.Time {
// NewLetsEncryptServer creates a new LetsEncryptServer instance
func NewLetsEncryptServer() *LetsEncryptServer {
return &LetsEncryptServer{
newCertChannel: make(chan string),
newCertChannel: make(chan newCertRequest),
lastModified: lastModifiedNow(),
}
}
......@@ -51,7 +56,7 @@ func (s *LetsEncryptServer) parseEnv() error {
return nil
}
func (s *LetsEncryptServer) newSslCert(domain string) error {
func (s *LetsEncryptServer) newSslCert(domain string, altDomains []string) error {
cmd := exec.Command(
"certbot",
"certonly",
......@@ -63,6 +68,9 @@ func (s *LetsEncryptServer) newSslCert(domain string) error {
"--webroot-path", certbotWebRootDir,
"--domain", domain,
)
for _, altDomain := range altDomains {
cmd.Args = append(cmd.Args, "--domain", altDomain)
}
if s.dryRun {
cmd.Args = append(cmd.Args, "--dry-run")
......@@ -129,9 +137,9 @@ func (s *LetsEncryptServer) checkIfCertExists(domain string) (bool, error) {
}
func (s *LetsEncryptServer) processNewCertRequests() {
for domain := range s.newCertChannel {
log.Printf("new cert request %v", domain)
if domain == "RENEW" {
for req := range s.newCertChannel {
log.Printf("new cert request %v", req.domain)
if req.domain == "RENEW" {
err := s.renewSslCerts()
if err != nil {
log.Printf("failed to renew certificates: %v", err)
......@@ -139,24 +147,21 @@ func (s *LetsEncryptServer) processNewCertRequests() {
}
continue
}
if domain == "PING" {
continue
}
exists, err := s.checkIfCertExists(domain)
exists, err := s.checkIfCertExists(req.domain)
if err != nil {
log.Printf("failed to check if certificate for %s already exists: %v", domain, err)
log.Printf("failed to check if certificate for %s already exists: %v", req.domain, err)
continue
}
if exists {
log.Printf("certificate for %s already exists, skipping", domain)
log.Printf("certificate for %s already exists, skipping", req.domain)
continue
}
err = s.newSslCert(domain)
err = s.newSslCert(req.domain, req.altDomains)
if err != nil {
log.Printf("failed to generate certificate for %s: %v", domain, err)
log.Printf("failed to generate certificate for %s: %v", req.domain, err)
continue
}
log.Printf("successfully generated certificate for %s", domain)
log.Printf("successfully generated certificate for %s", req.domain)
}
}
......@@ -196,7 +201,8 @@ func (s *LetsEncryptServer) handleDump(writer http.ResponseWriter, request *http
func (s *LetsEncryptServer) handleNewCert(writer http.ResponseWriter, request *http.Request) {
request.ParseForm()
domain := request.Form.Get("domain")
log.Printf("/new-cert?domain=%s request", domain)
altDomains := request.Form["alt_domains"]
log.Printf("/new-cert?%s request", request.Form.Encode())
if domain == "" {
writer.WriteHeader(http.StatusBadRequest)
......@@ -216,7 +222,10 @@ func (s *LetsEncryptServer) handleNewCert(writer http.ResponseWriter, request *h
return
}
s.newCertChannel <- domain
var r newCertRequest
r.domain = domain
r.altDomains = altDomains
s.newCertChannel <- r
writer.WriteHeader(http.StatusOK)
writer.Write([]byte("ok"))
......@@ -243,18 +252,13 @@ func (s *LetsEncryptServer) Run() error {
go func() {
for {
s.newCertChannel <- "RENEW"
var r newCertRequest
r.domain = "RENEW"
s.newCertChannel <- r
time.Sleep(1 * time.Hour)
}
}()
go func() {
for {
s.newCertChannel <- "PING"
time.Sleep(10 * time.Second)
}
}()
mux := http.NewServeMux()
mux.HandleFunc("/dump", s.handleDump)
mux.HandleFunc("/new-cert", s.handleNewCert)
......
......@@ -23,9 +23,10 @@ const haproxySSLDir = "/etc/haproxy/ssl/"
const haproxyConfigFile = "/etc/haproxy/haproxy.cfg"
type siteInfo struct {
Name string `json:"name"`
Domain string `json:"domain"`
DisableLetsEncrypt bool `json:"disable_letsencrypt"`
Name string `json:"name"`
Domain string `json:"domain"`
AltDomains []string `json:"alt_domains"`
DisableLetsEncrypt bool `json:"disable_letsencrypt"`
SSL sslCertInfo
Backends []siteBackendInfo `json:"backends"`
Routes []siteRouteInfo `json:"routes"`
......@@ -91,11 +92,19 @@ frontend fe_https
bind *:443 ssl crt {{$.SSLDir}} alpn h2,http/1.1
acl is_letsencrypt path_beg /.well-known/acme-challenge
use_backend be_letsencrypt if is_letsencrypt
{{range $site := .Sites -}}
{{"\t"}}acl domain_{{.Name}} ssl_fc_sni -i {{$site.Domain}}
{{range $site := .Sites}}
{{"\t"}}acl domain_{{.Name}} ssl_fc_sni -i {{.Domain}}
{{range $i, $domain := .AltDomains -}}
{{"\t"}}acl alt_domain_{{$site.Name}}_{{$i}} ssl_fc_sni -i {{.}}
{{end -}}
{{range $i, $route := .Routes -}}
{{"\t"}}acl route_{{$site.Name}}_{{$i}} path_beg {{.Path}}
{{end -}}
{{range $i, $route := $site.Routes -}}
{{"\t"}}use_backend be_{{$site.Name}}_{{.Backend}} if domain_{{$site.Name}} route_{{$site.Name}}_{{$i}}
{{range $j, $domain := $site.AltDomains -}}
{{"\t"}}use_backend be_{{$site.Name}}_{{$route.Backend}} if alt_domain_{{$site.Name}}_{{$j}} route_{{$site.Name}}_{{$i}}
{{end -}}
{{end}}
{{- end}}
......@@ -387,6 +396,9 @@ func (p *ProxyServer) updateSslCerts() (bool, error) {
}
form := url.Values{}
form.Set("domain", site.Domain)
for _, domain := range site.AltDomains {
form.Set("alt_domain", domain)
}
http.PostForm(newCertURL, form)
}
......
......@@ -9,8 +9,9 @@ import (
func TestRenderTemplate(t *testing.T) {
proxy := NewProxyServer()
proxy.Sites = append(proxy.Sites, &siteInfo{
Name: "example",
Domain: "example.com",
Name: "example",
Domain: "example.com",
AltDomains: []string{"www.example.com"},
SSL: sslCertInfo{
CertificatePath: "/etc/ssl/example.pem",
PrivateKeyPath: "/etc/ssl/private/example.key",
......@@ -112,11 +113,16 @@ frontend fe_https
bind *:443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1
acl is_letsencrypt path_beg /.well-known/acme-challenge
use_backend be_letsencrypt if is_letsencrypt
acl domain_example ssl_fc_sni -i example.com
acl alt_domain_example_0 ssl_fc_sni -i www.example.com
acl route_example_0 path_beg /api
use_backend be_example_api if domain_example route_example_0
acl route_example_1 path_beg /
use_backend be_example_api if domain_example route_example_0
use_backend be_example_api if alt_domain_example_0 route_example_0
use_backend be_example_web if domain_example route_example_1
use_backend be_example_web if alt_domain_example_0 route_example_1
acl domain_example2 ssl_fc_sni -i example2.com
acl route_example2_0 path_beg /
use_backend be_example2_default if domain_example2 route_example2_0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment