github.com/minio/console@v1.4.1/api/server.go (about) 1 //go:build exclude 2 3 // This file is generated by go-swagger but it will be 4 // replaced by a proper server to have more customization 5 // Currently, this file is replaced by custom-server.go 6 7 // Code generated by go-swagger; DO NOT EDIT. 8 9 // This file is part of MinIO Console Server 10 // Copyright (c) 2023 MinIO, Inc. 11 // 12 // This program is free software: you can redistribute it and/or modify 13 // it under the terms of the GNU Affero General Public License as published by 14 // the Free Software Foundation, either version 3 of the License, or 15 // (at your option) any later version. 16 // 17 // This program is distributed in the hope that it will be useful, 18 // but WITHOUT ANY WARRANTY; without even the implied warranty of 19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 // GNU Affero General Public License for more details. 21 // 22 // You should have received a copy of the GNU Affero General Public License 23 // along with this program. If not, see <http://www.gnu.org/licenses/>. 24 // 25 26 package api 27 28 import ( 29 "context" 30 "crypto/tls" 31 "crypto/x509" 32 "errors" 33 "fmt" 34 "log" 35 "net" 36 "net/http" 37 "os" 38 "os/signal" 39 "strconv" 40 "sync" 41 "sync/atomic" 42 "syscall" 43 "time" 44 45 "github.com/go-openapi/runtime/flagext" 46 "github.com/go-openapi/swag" 47 flags "github.com/jessevdk/go-flags" 48 "golang.org/x/net/netutil" 49 50 "github.com/minio/console/api/operations" 51 ) 52 53 const ( 54 schemeHTTP = "http" 55 schemeHTTPS = "https" 56 schemeUnix = "unix" 57 ) 58 59 var defaultSchemes []string 60 61 func init() { 62 defaultSchemes = []string{ 63 schemeHTTP, 64 } 65 } 66 67 // NewServer creates a new api console server but does not configure it 68 func NewServer(api *operations.ConsoleAPI) *Server { 69 s := new(Server) 70 71 s.shutdown = make(chan struct{}) 72 s.api = api 73 s.interrupt = make(chan os.Signal, 1) 74 return s 75 } 76 77 // ConfigureAPI configures the API and handlers. 78 func (s *Server) ConfigureAPI() { 79 if s.api != nil { 80 s.handler = configureAPI(s.api) 81 } 82 } 83 84 // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse 85 func (s *Server) ConfigureFlags() { 86 if s.api != nil { 87 configureFlags(s.api) 88 } 89 } 90 91 // Server for the console API 92 type Server struct { 93 EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"` 94 CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"` 95 GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"` 96 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"` 97 98 SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/console.sock"` 99 domainSocketL net.Listener 100 101 Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"` 102 Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"` 103 ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"` 104 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"` 105 ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"` 106 WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"` 107 httpServerL net.Listener 108 109 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"` 110 TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"` 111 TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"` 112 TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"` 113 TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"` 114 TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"` 115 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)"` 116 TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"` 117 TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"` 118 httpsServerL net.Listener 119 120 api *operations.ConsoleAPI 121 handler http.Handler 122 hasListeners bool 123 shutdown chan struct{} 124 shuttingDown int32 125 interrupted bool 126 interrupt chan os.Signal 127 } 128 129 // Logf logs message either via defined user logger or via system one if no user logger is defined. 130 func (s *Server) Logf(f string, args ...interface{}) { 131 if s.api != nil && s.api.Logger != nil { 132 s.api.Logger(f, args...) 133 } else { 134 log.Printf(f, args...) 135 } 136 } 137 138 // Fatalf logs message either via defined user logger or via system one if no user logger is defined. 139 // Exits with non-zero status after printing 140 func (s *Server) Fatalf(f string, args ...interface{}) { 141 if s.api != nil && s.api.Logger != nil { 142 s.api.Logger(f, args...) 143 os.Exit(1) 144 } else { 145 log.Fatalf(f, args...) 146 } 147 } 148 149 // SetAPI configures the server with the specified API. Needs to be called before Serve 150 func (s *Server) SetAPI(api *operations.ConsoleAPI) { 151 if api == nil { 152 s.api = nil 153 s.handler = nil 154 return 155 } 156 157 s.api = api 158 s.handler = configureAPI(api) 159 } 160 161 func (s *Server) hasScheme(scheme string) bool { 162 schemes := s.EnabledListeners 163 if len(schemes) == 0 { 164 schemes = defaultSchemes 165 } 166 167 for _, v := range schemes { 168 if v == scheme { 169 return true 170 } 171 } 172 return false 173 } 174 175 // Serve the api 176 func (s *Server) Serve() (err error) { 177 if !s.hasListeners { 178 if err = s.Listen(); err != nil { 179 return err 180 } 181 } 182 183 // set default handler, if none is set 184 if s.handler == nil { 185 if s.api == nil { 186 return errors.New("can't create the default handler, as no api is set") 187 } 188 189 s.SetHandler(s.api.Serve(nil)) 190 } 191 192 wg := new(sync.WaitGroup) 193 once := new(sync.Once) 194 signalNotify(s.interrupt) 195 go handleInterrupt(once, s) 196 197 servers := []*http.Server{} 198 199 if s.hasScheme(schemeUnix) { 200 domainSocket := new(http.Server) 201 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) 202 domainSocket.Handler = s.handler 203 if int64(s.CleanupTimeout) > 0 { 204 domainSocket.IdleTimeout = s.CleanupTimeout 205 } 206 207 configureServer(domainSocket, "unix", string(s.SocketPath)) 208 209 servers = append(servers, domainSocket) 210 wg.Add(1) 211 s.Logf("Serving console at unix://%s", s.SocketPath) 212 go func(l net.Listener) { 213 defer wg.Done() 214 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 215 s.Fatalf("%v", err) 216 } 217 s.Logf("Stopped serving console at unix://%s", s.SocketPath) 218 }(s.domainSocketL) 219 } 220 221 if s.hasScheme(schemeHTTP) { 222 httpServer := new(http.Server) 223 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) 224 httpServer.ReadTimeout = s.ReadTimeout 225 httpServer.WriteTimeout = s.WriteTimeout 226 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 227 if s.ListenLimit > 0 { 228 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 229 } 230 231 if int64(s.CleanupTimeout) > 0 { 232 httpServer.IdleTimeout = s.CleanupTimeout 233 } 234 235 httpServer.Handler = s.handler 236 237 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 238 239 servers = append(servers, httpServer) 240 wg.Add(1) 241 s.Logf("Serving console at http://%s", s.httpServerL.Addr()) 242 go func(l net.Listener) { 243 defer wg.Done() 244 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 245 s.Fatalf("%v", err) 246 } 247 s.Logf("Stopped serving console at http://%s", l.Addr()) 248 }(s.httpServerL) 249 } 250 251 if s.hasScheme(schemeHTTPS) { 252 httpsServer := new(http.Server) 253 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) 254 httpsServer.ReadTimeout = s.TLSReadTimeout 255 httpsServer.WriteTimeout = s.TLSWriteTimeout 256 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 257 if s.TLSListenLimit > 0 { 258 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 259 } 260 if int64(s.CleanupTimeout) > 0 { 261 httpsServer.IdleTimeout = s.CleanupTimeout 262 } 263 httpsServer.Handler = s.handler 264 265 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 266 httpsServer.TLSConfig = &tls.Config{ 267 // Causes servers to use Go's default ciphersuite preferences, 268 // which are tuned to avoid attacks. Does nothing on clients. 269 PreferServerCipherSuites: true, 270 // Only use curves which have assembly implementations 271 // https://github.com/golang/go/tree/master/src/crypto/elliptic 272 CurvePreferences: []tls.CurveID{tls.CurveP256}, 273 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 274 NextProtos: []string{"h2", "http/1.1"}, 275 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 276 MinVersion: tls.VersionTLS12, 277 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 278 CipherSuites: []uint16{ 279 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 280 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 281 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 282 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 283 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 284 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 285 }, 286 } 287 288 // build standard config from server options 289 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 290 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 291 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey)) 292 if err != nil { 293 return err 294 } 295 } 296 297 if s.TLSCACertificate != "" { 298 // include specified CA certificate 299 caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate)) 300 if caCertErr != nil { 301 return caCertErr 302 } 303 caCertPool := x509.NewCertPool() 304 ok := caCertPool.AppendCertsFromPEM(caCert) 305 if !ok { 306 return fmt.Errorf("cannot parse CA certificate") 307 } 308 httpsServer.TLSConfig.ClientCAs = caCertPool 309 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 310 } 311 312 // call custom TLS configurator 313 configureTLS(httpsServer.TLSConfig) 314 315 if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil { 316 // after standard and custom config are passed, this ends up with no certificate 317 if s.TLSCertificate == "" { 318 if s.TLSCertificateKey == "" { 319 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 320 } 321 s.Fatalf("the required flag `--tls-certificate` was not specified") 322 } 323 if s.TLSCertificateKey == "" { 324 s.Fatalf("the required flag `--tls-key` was not specified") 325 } 326 // this happens with a wrong custom TLS configurator 327 s.Fatalf("no certificate was configured for TLS") 328 } 329 330 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 331 332 servers = append(servers, httpsServer) 333 wg.Add(1) 334 s.Logf("Serving console at https://%s", s.httpsServerL.Addr()) 335 go func(l net.Listener) { 336 defer wg.Done() 337 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 338 s.Fatalf("%v", err) 339 } 340 s.Logf("Stopped serving console at https://%s", l.Addr()) 341 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 342 } 343 344 wg.Add(1) 345 go s.handleShutdown(wg, &servers) 346 347 wg.Wait() 348 return nil 349 } 350 351 // Listen creates the listeners for the server 352 func (s *Server) Listen() error { 353 if s.hasListeners { // already done this 354 return nil 355 } 356 357 if s.hasScheme(schemeHTTPS) { 358 // Use http host if https host wasn't defined 359 if s.TLSHost == "" { 360 s.TLSHost = s.Host 361 } 362 // Use http listen limit if https listen limit wasn't defined 363 if s.TLSListenLimit == 0 { 364 s.TLSListenLimit = s.ListenLimit 365 } 366 // Use http tcp keep alive if https tcp keep alive wasn't defined 367 if int64(s.TLSKeepAlive) == 0 { 368 s.TLSKeepAlive = s.KeepAlive 369 } 370 // Use http read timeout if https read timeout wasn't defined 371 if int64(s.TLSReadTimeout) == 0 { 372 s.TLSReadTimeout = s.ReadTimeout 373 } 374 // Use http write timeout if https write timeout wasn't defined 375 if int64(s.TLSWriteTimeout) == 0 { 376 s.TLSWriteTimeout = s.WriteTimeout 377 } 378 } 379 380 if s.hasScheme(schemeUnix) { 381 domSockListener, err := net.Listen("unix", string(s.SocketPath)) 382 if err != nil { 383 return err 384 } 385 s.domainSocketL = domSockListener 386 } 387 388 if s.hasScheme(schemeHTTP) { 389 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 390 if err != nil { 391 return err 392 } 393 394 h, p, err := swag.SplitHostPort(listener.Addr().String()) 395 if err != nil { 396 return err 397 } 398 s.Host = h 399 s.Port = p 400 s.httpServerL = listener 401 } 402 403 if s.hasScheme(schemeHTTPS) { 404 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 405 if err != nil { 406 return err 407 } 408 409 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 410 if err != nil { 411 return err 412 } 413 s.TLSHost = sh 414 s.TLSPort = sp 415 s.httpsServerL = tlsListener 416 } 417 418 s.hasListeners = true 419 return nil 420 } 421 422 // Shutdown server and clean up resources 423 func (s *Server) Shutdown() error { 424 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { 425 close(s.shutdown) 426 } 427 return nil 428 } 429 430 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { 431 // wg.Done must occur last, after s.api.ServerShutdown() 432 // (to preserve old behaviour) 433 defer wg.Done() 434 435 <-s.shutdown 436 437 servers := *serversPtr 438 439 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 440 defer cancel() 441 442 // first execute the pre-shutdown hook 443 s.api.PreServerShutdown() 444 445 shutdownChan := make(chan bool) 446 for i := range servers { 447 server := servers[i] 448 go func() { 449 var success bool 450 defer func() { 451 shutdownChan <- success 452 }() 453 if err := server.Shutdown(ctx); err != nil { 454 // Error from closing listeners, or context timeout: 455 s.Logf("HTTP server Shutdown: %v", err) 456 } else { 457 success = true 458 } 459 }() 460 } 461 462 // Wait until all listeners have successfully shut down before calling ServerShutdown 463 success := true 464 for range servers { 465 success = success && <-shutdownChan 466 } 467 if success { 468 s.api.ServerShutdown() 469 } 470 } 471 472 // GetHandler returns a handler useful for testing 473 func (s *Server) GetHandler() http.Handler { 474 return s.handler 475 } 476 477 // SetHandler allows for setting a http handler on this server 478 func (s *Server) SetHandler(handler http.Handler) { 479 s.handler = handler 480 } 481 482 // UnixListener returns the domain socket listener 483 func (s *Server) UnixListener() (net.Listener, error) { 484 if !s.hasListeners { 485 if err := s.Listen(); err != nil { 486 return nil, err 487 } 488 } 489 return s.domainSocketL, nil 490 } 491 492 // HTTPListener returns the http listener 493 func (s *Server) HTTPListener() (net.Listener, error) { 494 if !s.hasListeners { 495 if err := s.Listen(); err != nil { 496 return nil, err 497 } 498 } 499 return s.httpServerL, nil 500 } 501 502 // TLSListener returns the https listener 503 func (s *Server) TLSListener() (net.Listener, error) { 504 if !s.hasListeners { 505 if err := s.Listen(); err != nil { 506 return nil, err 507 } 508 } 509 return s.httpsServerL, nil 510 } 511 512 func handleInterrupt(once *sync.Once, s *Server) { 513 once.Do(func() { 514 for range s.interrupt { 515 if s.interrupted { 516 s.Logf("Server already shutting down") 517 continue 518 } 519 s.interrupted = true 520 s.Logf("Shutting down... ") 521 if err := s.Shutdown(); err != nil { 522 s.Logf("HTTP server Shutdown: %v", err) 523 } 524 } 525 }) 526 } 527 528 func signalNotify(interrupt chan<- os.Signal) { 529 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 530 }