-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathproxy_main.go
More file actions
158 lines (145 loc) · 4.84 KB
/
Copy pathproxy_main.go
File metadata and controls
158 lines (145 loc) · 4.84 KB
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
// Fortio TLS Reverse Proxy.
//
// (c) 2022 Laurent Demailly
// See LICENSE
package main
import (
"context"
"crypto/tls"
"flag"
"fmt"
"net"
"net/http"
"os"
"strings"
"time"
"fortio.org/cli"
"fortio.org/dflag"
"fortio.org/fortio/fhttp"
"fortio.org/log"
"fortio.org/proxy/config"
"fortio.org/proxy/rp"
"fortio.org/scli"
"golang.org/x/crypto/acme/autocert"
)
const (
disabled = "disabled"
)
var (
email = dflag.DynString(flag.CommandLine, "email", "", "`Email` to attach to cert requests.")
certsFor = dflag.DynStringSet(flag.CommandLine, "certs-domains", []string{},
"Coma separated list of `domains` to get certs for")
certsDirectory = flag.String("certs-directory", ".", "Directory `path` where to store the certs")
port = flag.String("https-port", ":443", "`port` to listen on for main reverse proxy and tls traffic")
redirect = flag.String("redirect-port", ":80", "`port` to listen on for redirection")
httpPort = flag.String("http-port", disabled, "`port` to listen on for non tls traffic (or 'disabled')")
autoTailscale = flag.Bool("tailscale", false, "Automatically add tailscale hostname to the certificate list")
timeout = flag.Duration("timeout", 1*time.Minute,
"Maximum duration for each request read/writes proxying (eg 1h or use 0 for no timeout)")
acert *autocert.Manager
tailscale string
)
func hostPolicy(_ context.Context, host string) error {
log.LogVf("cert host policy called for %q", host)
if tailscale != "" && host == tailscale {
return nil
}
allowed := certsFor.Get()
if _, found := allowed[host]; found {
return nil
}
return fmt.Errorf("acme/autocert: %q not in allowed list", host)
}
func debugGetCert(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
// Note: hello.ServerName is already lowercase.
isTailscale := config.IsTailscale(hello.ServerName)
log.LogVf("GetCert from %s for %q (tailscale %t)",
hello.Conn.RemoteAddr().String(), hello.ServerName, isTailscale)
if isTailscale {
if err := hostPolicy(context.Background(), hello.ServerName); err != nil {
return nil, err
}
return config.Tailscale().GetCertificate(hello)
}
return acert.GetCertificate(hello)
}
func main() {
cli.ProgramName = "Fortio proxy"
scli.ServerMain()
// Only turns on debug host if configured at launch,
// can be turned off or changed later through dynamic flags but not turned on if starting off
debugHost := rp.DebugHost.Get()
if *redirect != disabled {
var a net.Addr
if debugHost != "" {
// Special case for debug host, redirect to https but also serve debug on that host
a = fhttp.HTTPServerWithHandler("https redirector + debug", *redirect, rp.DebugOnHostHandler(fhttp.RedirectToHTTPSHandler))
} else {
// Standard redirector without special debug host case
a = fhttp.RedirectToHTTPS(*redirect)
}
if a == nil {
os.Exit(1) // Error already logged
}
}
if *autoTailscale {
tailscale = config.TailscaleServerName()
if tailscale == "" {
os.Exit(1) // Error already logged
}
log.S(log.Info, "Will accept TLS requests and obtain certificate for tailscale", log.Any("server-name", tailscale))
}
// Main reverse proxy handler (with debug if configured)
var hdlr http.Handler
hdlr = rp.ReverseProxy()
if debugHost != "" {
log.Warnf("Running Debug echo handler for any request matching Host %q", debugHost)
hdlr = rp.DebugOnHostHandler(hdlr.ServeHTTP) // that's the reverse proxy + debug handler
}
s := &http.Server{
ReadTimeout: *timeout,
WriteTimeout: *timeout,
IdleTimeout: 15 * time.Second,
ReadHeaderTimeout: 3 * time.Second, // reasonably small as the header are sent quickly for valid clients.
// The reverse proxy (+debug if configured)
Handler: hdlr,
ErrorLog: log.NewStdLogger("rp", log.Error),
}
log.Printf("Fortio Proxy %s started - hostid %q - tailscale capable: %t, on: %t",
cli.LongVersion, rp.HostID.Get(), config.HasTailscale, *autoTailscale)
if *httpPort != disabled {
fhttp.HTTPServerWithHandler("http-reverse-proxy", *httpPort, hdlr)
}
if *port == disabled {
log.Infof("No TLS server port.")
} else {
go startTLSProxy(s)
}
scli.UntilInterrupted()
}
func startTLSProxy(s *http.Server) {
s.Addr = *port
emailStr := strings.TrimSpace(email.Get())
acert = &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: hostPolicy,
Cache: autocert.DirCache(*certsDirectory),
Email: emailStr,
}
tlsCfg := acert.TLSConfig()
tlsCfg.GetCertificate = debugGetCert
tlsCfg.MinVersion = tls.VersionTLS12
s.TLSConfig = tlsCfg
currentMap := certsFor.Get()
currentDomains := make([]string, len(currentMap))
i := 0
for k := range currentMap {
currentDomains[i] = k
i++
}
log.Infof("Starting TLS on %s for %v (%s) - certs directory %s", *port, currentDomains, acert.Email, *certsDirectory)
err := s.ListenAndServeTLS("", "")
if err != nil {
log.Fatalf("ListendAndServeTLS(): %v", err)
}
}