This repository has been archived by the owner on Jul 16, 2019. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
141 lines (118 loc) · 3.57 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main
//go:generate make bindata
import (
"io"
"log"
"net/http"
"github.com/Luzifer/dockerproxy/sni"
"github.com/Luzifer/rconfig"
"github.com/hydrogen18/stoppableListener"
"github.com/robfig/cron"
)
var (
cfg = struct {
ConfigFile string `flag:"configfile" default:"./config.json" description:"Location of the configuration file"`
LetsEncryptServer string `flag:"letsencrypt-server" default:"https://acme-v01.api.letsencrypt.org/directory" description:"ACME directory endpoint"`
}{}
containers *dockerContainers
proxyConfiguration *proxyConfig
leClient *letsEncryptClient
sniServer = sni.SNIServer{}
)
func init() {
var err error
if err := rconfig.Parse(&cfg); err != nil {
log.Fatalf("Unable to parse commandline flags: %s", err)
}
proxyConfiguration, err = newProxyConfig(cfg.ConfigFile)
if err != nil {
log.Fatalf("Unable to parse configuration: %s", err)
}
leClient, err = newLetsEncryptClient(cfg.LetsEncryptServer)
if err != nil {
log.Fatalf("Unable to create LetsEncrypt client: %s", err)
}
}
func startSSLServer(proxy *dockerProxy, serverErrorChan chan error) {
// Collect certificates from disk
certificates := proxy.getCertificates()
// Get a certificate for all LetsEncrypt enabled domains
leDomains := []string{}
for domain, domainCFG := range proxyConfiguration.Domains {
if domainCFG.UseLetsEncrypt {
leDomains = append(leDomains, domain)
}
}
if len(leDomains) > 0 {
cert, key, err := leClient.FetchMultiDomainCertificate(leDomains)
if err != nil {
log.Fatalf("ERROR: Unable to get certificate: %s", err)
}
intermediate, err := leClient.GetIntermediateCertificate()
if err != nil {
log.Fatalf("ERROR: Unable to get intermediate certificate: %s", err)
}
certificates = append(certificates, sni.Certificates{
Certificate: cert,
Key: key,
Intermediate: intermediate,
})
}
go func(proxy *dockerProxy, certificates []sni.Certificates) {
httpsServer := &http.Server{
Handler: proxy,
Addr: proxyConfiguration.ListenHTTPS,
}
serverErrorChan <- sniServer.ListenAndServeTLSSNI(httpsServer, certificates)
}(proxy, certificates)
}
func startHTTPServer(proxy *dockerProxy, serverErrorChan chan error) {
letsEncryptHandler := http.HandlerFunc(func(res http.ResponseWriter, r *http.Request) {
if challenge, ok := leClient.Challenges[r.Host]; ok {
if r.URL.RequestURI() == challenge.Path {
log.Printf("Got challenge request for domain %s and answered.", r.Host)
io.WriteString(res, challenge.Response)
return
}
}
proxy.ServeHTTP(res, r)
})
go func(h http.Handler) {
serverErrorChan <- http.ListenAndServe(proxyConfiguration.ListenHTTP, h)
}(letsEncryptHandler)
}
func reloadConfiguration() {
tmp, err := newProxyConfig(cfg.ConfigFile)
if err == nil {
proxyConfiguration = tmp
} else {
log.Printf("%v\n", err)
}
containers = collectDockerContainer()
}
func main() {
containers = collectDockerContainer()
proxy := newDockerProxy()
c := cron.New()
c.AddFunc("@every 1m", reloadConfiguration)
c.AddFunc("@every 720h", func() {
// Stop the SNI server every 30d, it will get restarted and
// the LetsEncrypt certificates are checked for expiry
sniServer.Stop()
})
c.Start()
serverErrorChan := make(chan error, 2)
startHTTPServer(proxy, serverErrorChan)
startSSLServer(proxy, serverErrorChan)
for {
select {
case err := <-serverErrorChan:
if err != stoppableListener.StoppedError {
log.Fatal(err)
} else {
// Something made the SNI server stop, we just restart it
startSSLServer(proxy, serverErrorChan)
}
}
}
}