1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package restapi
18
19 import (
20 "context"
21 "crypto/tls"
22 "crypto/x509"
23 "errors"
24 "fmt"
25 "log"
26 "net"
27 "net/http"
28 "os"
29 "os/signal"
30 "strconv"
31 "sync"
32 "sync/atomic"
33 "syscall"
34 "time"
35
36 "github.com/go-openapi/runtime/flagext"
37 "github.com/go-openapi/swag"
38 flags "github.com/jessevdk/go-flags"
39 "golang.org/x/net/netutil"
40
41 "github.com/prometheus/alertmanager/api/v2/restapi/operations"
42 )
43
44 const (
45 schemeHTTP = "http"
46 schemeHTTPS = "https"
47 schemeUnix = "unix"
48 )
49
50 var defaultSchemes []string
51
52 func init() {
53 defaultSchemes = []string{
54 schemeHTTP,
55 }
56 }
57
58
59 func NewServer(api *operations.AlertmanagerAPI) *Server {
60 s := new(Server)
61
62 s.shutdown = make(chan struct{})
63 s.api = api
64 s.interrupt = make(chan os.Signal, 1)
65 return s
66 }
67
68
69 func (s *Server) ConfigureAPI() {
70 if s.api != nil {
71 s.handler = configureAPI(s.api)
72 }
73 }
74
75
76 func (s *Server) ConfigureFlags() {
77 if s.api != nil {
78 configureFlags(s.api)
79 }
80 }
81
82
83 type Server struct {
84 EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
85 CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
86 GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
87 MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
88
89 SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/alertmanager.sock"`
90 domainSocketL net.Listener
91
92 Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
93 Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
94 ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
95 KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
96 ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
97 WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
98 httpServerL net.Listener
99
100 TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
101 TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
102 TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
103 TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
104 TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
105 TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
106 TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
107 TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
108 TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
109 httpsServerL net.Listener
110
111 api *operations.AlertmanagerAPI
112 handler http.Handler
113 hasListeners bool
114 shutdown chan struct{}
115 shuttingDown int32
116 interrupted bool
117 interrupt chan os.Signal
118 }
119
120
121 func (s *Server) Logf(f string, args ...interface{}) {
122 if s.api != nil && s.api.Logger != nil {
123 s.api.Logger(f, args...)
124 } else {
125 log.Printf(f, args...)
126 }
127 }
128
129
130
131 func (s *Server) Fatalf(f string, args ...interface{}) {
132 if s.api != nil && s.api.Logger != nil {
133 s.api.Logger(f, args...)
134 os.Exit(1)
135 } else {
136 log.Fatalf(f, args...)
137 }
138 }
139
140
141 func (s *Server) SetAPI(api *operations.AlertmanagerAPI) {
142 if api == nil {
143 s.api = nil
144 s.handler = nil
145 return
146 }
147
148 s.api = api
149 s.handler = configureAPI(api)
150 }
151
152 func (s *Server) hasScheme(scheme string) bool {
153 schemes := s.EnabledListeners
154 if len(schemes) == 0 {
155 schemes = defaultSchemes
156 }
157
158 for _, v := range schemes {
159 if v == scheme {
160 return true
161 }
162 }
163 return false
164 }
165
166
167 func (s *Server) Serve() (err error) {
168 if !s.hasListeners {
169 if err = s.Listen(); err != nil {
170 return err
171 }
172 }
173
174
175 if s.handler == nil {
176 if s.api == nil {
177 return errors.New("can't create the default handler, as no api is set")
178 }
179
180 s.SetHandler(s.api.Serve(nil))
181 }
182
183 wg := new(sync.WaitGroup)
184 once := new(sync.Once)
185 signalNotify(s.interrupt)
186 go handleInterrupt(once, s)
187
188 servers := []*http.Server{}
189
190 if s.hasScheme(schemeUnix) {
191 domainSocket := new(http.Server)
192 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
193 domainSocket.Handler = s.handler
194 if int64(s.CleanupTimeout) > 0 {
195 domainSocket.IdleTimeout = s.CleanupTimeout
196 }
197
198 configureServer(domainSocket, "unix", string(s.SocketPath))
199
200 servers = append(servers, domainSocket)
201 wg.Add(1)
202 s.Logf("Serving alertmanager at unix://%s", s.SocketPath)
203 go func(l net.Listener) {
204 defer wg.Done()
205 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
206 s.Fatalf("%v", err)
207 }
208 s.Logf("Stopped serving alertmanager at unix://%s", s.SocketPath)
209 }(s.domainSocketL)
210 }
211
212 if s.hasScheme(schemeHTTP) {
213 httpServer := new(http.Server)
214 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
215 httpServer.ReadTimeout = s.ReadTimeout
216 httpServer.WriteTimeout = s.WriteTimeout
217 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
218 if s.ListenLimit > 0 {
219 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
220 }
221
222 if int64(s.CleanupTimeout) > 0 {
223 httpServer.IdleTimeout = s.CleanupTimeout
224 }
225
226 httpServer.Handler = s.handler
227
228 configureServer(httpServer, "http", s.httpServerL.Addr().String())
229
230 servers = append(servers, httpServer)
231 wg.Add(1)
232 s.Logf("Serving alertmanager at http://%s", s.httpServerL.Addr())
233 go func(l net.Listener) {
234 defer wg.Done()
235 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
236 s.Fatalf("%v", err)
237 }
238 s.Logf("Stopped serving alertmanager at http://%s", l.Addr())
239 }(s.httpServerL)
240 }
241
242 if s.hasScheme(schemeHTTPS) {
243 httpsServer := new(http.Server)
244 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
245 httpsServer.ReadTimeout = s.TLSReadTimeout
246 httpsServer.WriteTimeout = s.TLSWriteTimeout
247 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
248 if s.TLSListenLimit > 0 {
249 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
250 }
251 if int64(s.CleanupTimeout) > 0 {
252 httpsServer.IdleTimeout = s.CleanupTimeout
253 }
254 httpsServer.Handler = s.handler
255
256
257 httpsServer.TLSConfig = &tls.Config{
258
259
260 PreferServerCipherSuites: true,
261
262
263 CurvePreferences: []tls.CurveID{tls.CurveP256},
264
265 NextProtos: []string{"h2", "http/1.1"},
266
267 MinVersion: tls.VersionTLS12,
268
269 CipherSuites: []uint16{
270 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
271 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
272 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
273 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
274 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
275 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
276 },
277 }
278
279
280 if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
281 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
282 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
283 if err != nil {
284 return err
285 }
286 }
287
288 if s.TLSCACertificate != "" {
289
290 caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate))
291 if caCertErr != nil {
292 return caCertErr
293 }
294 caCertPool := x509.NewCertPool()
295 ok := caCertPool.AppendCertsFromPEM(caCert)
296 if !ok {
297 return fmt.Errorf("cannot parse CA certificate")
298 }
299 httpsServer.TLSConfig.ClientCAs = caCertPool
300 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
301 }
302
303
304 configureTLS(httpsServer.TLSConfig)
305
306 if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
307
308 if s.TLSCertificate == "" {
309 if s.TLSCertificateKey == "" {
310 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
311 }
312 s.Fatalf("the required flag `--tls-certificate` was not specified")
313 }
314 if s.TLSCertificateKey == "" {
315 s.Fatalf("the required flag `--tls-key` was not specified")
316 }
317
318 s.Fatalf("no certificate was configured for TLS")
319 }
320
321 configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
322
323 servers = append(servers, httpsServer)
324 wg.Add(1)
325 s.Logf("Serving alertmanager at https://%s", s.httpsServerL.Addr())
326 go func(l net.Listener) {
327 defer wg.Done()
328 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
329 s.Fatalf("%v", err)
330 }
331 s.Logf("Stopped serving alertmanager at https://%s", l.Addr())
332 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
333 }
334
335 wg.Add(1)
336 go s.handleShutdown(wg, &servers)
337
338 wg.Wait()
339 return nil
340 }
341
342
343 func (s *Server) Listen() error {
344 if s.hasListeners {
345 return nil
346 }
347
348 if s.hasScheme(schemeHTTPS) {
349
350 if s.TLSHost == "" {
351 s.TLSHost = s.Host
352 }
353
354 if s.TLSListenLimit == 0 {
355 s.TLSListenLimit = s.ListenLimit
356 }
357
358 if int64(s.TLSKeepAlive) == 0 {
359 s.TLSKeepAlive = s.KeepAlive
360 }
361
362 if int64(s.TLSReadTimeout) == 0 {
363 s.TLSReadTimeout = s.ReadTimeout
364 }
365
366 if int64(s.TLSWriteTimeout) == 0 {
367 s.TLSWriteTimeout = s.WriteTimeout
368 }
369 }
370
371 if s.hasScheme(schemeUnix) {
372 domSockListener, err := net.Listen("unix", string(s.SocketPath))
373 if err != nil {
374 return err
375 }
376 s.domainSocketL = domSockListener
377 }
378
379 if s.hasScheme(schemeHTTP) {
380 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
381 if err != nil {
382 return err
383 }
384
385 h, p, err := swag.SplitHostPort(listener.Addr().String())
386 if err != nil {
387 return err
388 }
389 s.Host = h
390 s.Port = p
391 s.httpServerL = listener
392 }
393
394 if s.hasScheme(schemeHTTPS) {
395 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
396 if err != nil {
397 return err
398 }
399
400 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
401 if err != nil {
402 return err
403 }
404 s.TLSHost = sh
405 s.TLSPort = sp
406 s.httpsServerL = tlsListener
407 }
408
409 s.hasListeners = true
410 return nil
411 }
412
413
414 func (s *Server) Shutdown() error {
415 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
416 close(s.shutdown)
417 }
418 return nil
419 }
420
421 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
422
423
424 defer wg.Done()
425
426 <-s.shutdown
427
428 servers := *serversPtr
429
430 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
431 defer cancel()
432
433
434 s.api.PreServerShutdown()
435
436 shutdownChan := make(chan bool)
437 for i := range servers {
438 server := servers[i]
439 go func() {
440 var success bool
441 defer func() {
442 shutdownChan <- success
443 }()
444 if err := server.Shutdown(ctx); err != nil {
445
446 s.Logf("HTTP server Shutdown: %v", err)
447 } else {
448 success = true
449 }
450 }()
451 }
452
453
454 success := true
455 for range servers {
456 success = success && <-shutdownChan
457 }
458 if success {
459 s.api.ServerShutdown()
460 }
461 }
462
463
464 func (s *Server) GetHandler() http.Handler {
465 return s.handler
466 }
467
468
469 func (s *Server) SetHandler(handler http.Handler) {
470 s.handler = handler
471 }
472
473
474 func (s *Server) UnixListener() (net.Listener, error) {
475 if !s.hasListeners {
476 if err := s.Listen(); err != nil {
477 return nil, err
478 }
479 }
480 return s.domainSocketL, nil
481 }
482
483
484 func (s *Server) HTTPListener() (net.Listener, error) {
485 if !s.hasListeners {
486 if err := s.Listen(); err != nil {
487 return nil, err
488 }
489 }
490 return s.httpServerL, nil
491 }
492
493
494 func (s *Server) TLSListener() (net.Listener, error) {
495 if !s.hasListeners {
496 if err := s.Listen(); err != nil {
497 return nil, err
498 }
499 }
500 return s.httpsServerL, nil
501 }
502
503 func handleInterrupt(once *sync.Once, s *Server) {
504 once.Do(func() {
505 for range s.interrupt {
506 if s.interrupted {
507 s.Logf("Server already shutting down")
508 continue
509 }
510 s.interrupted = true
511 s.Logf("Shutting down... ")
512 if err := s.Shutdown(); err != nil {
513 s.Logf("HTTP server Shutdown: %v", err)
514 }
515 }
516 })
517 }
518
519 func signalNotify(interrupt chan<- os.Signal) {
520 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
521 }
522
View as plain text