-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
187 lines (162 loc) · 4.85 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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package main
import (
"crypto/tls"
"embed"
"flag"
"fmt"
"io"
"io/fs"
"log/slog"
"net/http"
"os"
"strings"
"time"
)
const (
defaultHTTPAddr = ":8080"
defaultHTTPSAddr = ":8443"
)
type Config struct {
Live bool
HTTPAddr string
HTTPSAddr string
CertFile string
KeyFile string
KeyLogFile string
}
var (
//go:embed apps/*
staticFiles embed.FS
// File system object to serve either live files from parsedStaticFiles or embedded files.
files fs.FS
// Environment variables used as context info in the echo response.
envContext map[string]string
)
func newConfig() *Config {
live := flag.Bool("live", false, "Serve live static files")
httpAddr := flag.String("http-addr", "", "Address to bind the HTTP server socket")
httpsAddr := flag.String("https-addr", "", "Address to bind the HTTPS server socket")
certFile := flag.String("tls-cert-file", "", "Path to TLS certificate file")
keyFile := flag.String("tls-key-file", "", "Path to TLS key file")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "\nEnvironment variables:\n")
fmt.Fprintf(os.Stderr, " HTTP_ADDR\n\tAddress to bind the HTTP server socket\n")
fmt.Fprintf(os.Stderr, " HTTPS_ADDR\n\tAddress to bind the HTTPS server socket\n")
fmt.Fprintf(os.Stderr, " TLS_CERT_FILE\n\tPath to TLS certificate file\n")
fmt.Fprintf(os.Stderr, " TLS_KEY_FILE\n\tPath to TLS key file\n")
fmt.Fprintf(os.Stderr, " ENV_*\n\tEnvironment variables to be used as context info in the echo response\n")
fmt.Fprintf(os.Stderr, " SSLKEYLOGFILE\n\tPath to write the TLS master secret log file\n")
}
flag.Parse()
getEnv := func(key, flagValue, defaultValue string) string {
if flagValue != "" {
return flagValue
}
if value, exists := os.LookupEnv(key); exists {
return value
}
return defaultValue
}
return &Config{
Live: *live,
HTTPAddr: getEnv("HTTP_ADDR", *httpAddr, defaultHTTPAddr),
HTTPSAddr: getEnv("HTTPS_ADDR", *httpsAddr, defaultHTTPSAddr),
CertFile: getEnv("TLS_CERT_FILE", *certFile, ""),
KeyFile: getEnv("TLS_KEY_FILE", *keyFile, ""),
KeyLogFile: os.Getenv("SSLKEYLOGFILE"),
}
}
func setupFilesystem(live bool) {
if live {
slog.Info("Serving live static files")
files = os.DirFS("./apps")
} else {
slog.Info("Serving embedded static files")
files, _ = fs.Sub(staticFiles, "apps")
}
}
func startHTTPServer(addr string) {
server := &http.Server{
Addr: addr,
ReadHeaderTimeout: time.Duration(5) * time.Second,
}
slog.Info("Server is running in HTTP mode", "address", server.Addr)
err := server.ListenAndServe()
if err != nil {
slog.Error("Error starting HTTP server", "error", err)
}
}
func startHTTPSServer(addr string, certFile, keyFile string, keyLogFile string) {
lookupCert := func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
slog.Error("Error loading certificate", "error", err)
return nil, err
}
return &cert, nil
}
// Call lookupCert to check if the certificate and key files are valid.
_, err := lookupCert(nil)
if err != nil {
os.Exit(1)
}
var keyLogWriter io.Writer
if keyLogFile != "" {
keyLogWriter, err = os.OpenFile(keyLogFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o600)
if err != nil {
slog.Error("Error opening key log file", "error", err)
os.Exit(1)
}
slog.Info("Enabling TLS master secret logging", "SSLKEYLOGFILE", keyLogFile)
}
server := &http.Server{
Addr: addr,
ReadHeaderTimeout: time.Duration(5) * time.Second,
}
server.TLSConfig = &tls.Config{ // #nosec // G402: TLS MinVersion too low.
ClientAuth: tls.RequestClientCert,
GetCertificate: lookupCert,
KeyLogWriter: keyLogWriter,
}
slog.Info("Server is running in HTTPS mode", "address", server.Addr,
"tls_cert_file", certFile, "tls_key_file", keyFile)
err = server.ListenAndServeTLS("", "")
if err != nil {
slog.Error("Error starting HTTPS server", "error", err)
}
}
// parseEnvContext reads environment variables that start with "ENV_" and stores them to be used
// as context info in the echo response.
func parseEnvContext() {
const prefix = "ENV_"
env := map[string]string{}
var envLog []any
for _, e := range os.Environ() {
if strings.HasPrefix(e, prefix) {
pair := strings.SplitN(e, "=", 2)
env[pair[0]] = pair[1]
envLog = append(envLog, pair[0], pair[1])
}
}
if len(env) > 0 {
envContext = env
slog.Info("Environment context", envLog...)
}
}
func main() {
config := newConfig()
setupFilesystem(config.Live)
parseEnvContext()
handler := &Handler{
files: files,
envContext: envContext,
}
http.Handle("/", handler)
if config.CertFile != "" && config.KeyFile != "" {
go startHTTPSServer(config.HTTPSAddr, config.CertFile, config.KeyFile, config.KeyLogFile)
}
go startHTTPServer(config.HTTPAddr)
select {}
}