-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
158 lines (132 loc) · 3.94 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package main
import (
"crypto/tls"
"log"
"net"
"net/http"
"os"
"strings"
"golang.org/x/crypto/acme/autocert"
)
var (
redirectDomains map[string]int
useCache bool
)
func init() {
initRedirectDomains()
initDNS()
initStatic()
}
func initRedirectDomains() {
redirectDomains = make(map[string]int)
domainEnvToStatus := map[string]int{
"REDIRECT_DOMAIN_301": http.StatusMovedPermanently,
"REDIRECT_DOMAIN_302": http.StatusFound,
"REDIRECT_DOMAIN_303": http.StatusSeeOther,
"REDIRECT_DOMAIN_307": http.StatusTemporaryRedirect,
"REDIRECT_DOMAIN_308": http.StatusPermanentRedirect,
}
for envVar, status := range domainEnvToStatus {
suffix := os.Getenv(envVar)
if suffix != "" {
redirectDomains[suffix] = status
log.Printf("Redirect domain suffix: %s, Status code: %d", suffix, status)
}
}
if len(redirectDomains) == 0 {
log.Fatal("No redirect domains defined. Please set at least one REDIRECT_DOMAIN_ environment variable.")
}
}
func handler(w http.ResponseWriter, r *http.Request) {
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
host = r.Host
}
// When visiting a redirect domain directly, serve the static page
if _, exists := redirectDomains[host]; exists {
serveStaticPage(w, r)
return
}
// Fetch the CNAME record for the host
cname, err := getCNAME(host)
if err != nil {
cname, err = getCNAME("@." + host)
}
if err != nil {
// If there is an error, log it and return an error to the user
log.Printf("Error fetching CNAME for %s: %v", host, err)
http.Error(w, "Unable to fetch CNAME", http.StatusInternalServerError)
return
}
// Remove the trailing dot from the FQDN
cname = cname[:len(cname)-1]
// Determine the appropriate redirect domain and status code
var targetDomain string
var statusCode int
for suffix, code := range redirectDomains {
if strings.HasSuffix(cname, "."+suffix) {
// Remove the redirect domain suffix from the CNAME
targetDomain = strings.TrimSuffix(cname, "."+suffix)
statusCode = code
break
}
}
if targetDomain == "" {
log.Printf("Unexpected CNAME format or no matching redirect domain: %s", cname)
http.Error(w, "Unexpected CNAME format", http.StatusInternalServerError)
return
}
// Determine the scheme (http or https) based on the request
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
// Construct the target URL
target := scheme + "://" + targetDomain + r.URL.String()
// Redirect to the target with the appropriate status code
log.Printf("Redirect (%d) %s to %s", statusCode, scheme+"://"+r.Host+r.URL.String(), target)
http.Redirect(w, r, target, statusCode)
}
func startHttpServer(certManager *autocert.Manager) {
httpPort := os.Getenv("HTTP_PORT")
if httpPort == "" {
httpPort = "80"
}
// Use the certManager's HTTPHandler to handle ACME challenges for Let's Encrypt
log.Printf("Starting HTTP server on :%s", httpPort)
log.Fatal(http.ListenAndServe(":"+httpPort, certManager.HTTPHandler(http.HandlerFunc(handler))))
}
func startHttpsServer(certManager *autocert.Manager) {
httpPort := os.Getenv("HTTP_PORT")
httpsPort := os.Getenv("HTTPS_PORT")
if httpsPort == "" {
if httpPort != "" && httpPort != "80" {
return
}
httpsPort = "443"
}
if httpPort != "" && httpPort != "80" {
log.Fatal("Unable to serve HTTPS traffic if HTTP is not served on port 80 due to Let's Encrypt challenge")
}
// HTTPS server with Let's Encrypt
server := &http.Server{
Addr: ":" + httpsPort,
Handler: http.HandlerFunc(handler),
TLSConfig: &tls.Config{
GetCertificate: certManager.GetCertificate,
},
}
// Start the HTTPS server
log.Printf("Starting HTTPS server on :%s", httpsPort)
log.Fatal(server.ListenAndServeTLS("", ""))
}
func main() {
// Create an autocert manager for Let's Encrypt certificates
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
Cache: autocert.DirCache("certs"), // Folder for storing certificates
}
go startHttpServer(&certManager)
go startHttpsServer(&certManager)
select {}
}