github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/server/https/https.go (about)

     1  // Copyright 2017 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package https implements an https-based server Communicator. It is the
    16  // primary way for clients to communicate with the FS server.
    17  package https
    18  
    19  import (
    20  	"crypto/tls"
    21  	"net"
    22  	"net/http"
    23  	"sync"
    24  	"time"
    25  
    26  	log "github.com/golang/glog"
    27  
    28  	"github.com/google/fleetspeak/fleetspeak/src/server/authorizer"
    29  	"github.com/google/fleetspeak/fleetspeak/src/server/comms"
    30  
    31  	cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components"
    32  )
    33  
    34  const (
    35  	// MaxContactSize is the largest contact (in bytes) that we will accept.
    36  	MaxContactSize = 20 * 1024 * 1024
    37  )
    38  
    39  // Communicator implements server.Communicator, and accepts client connections
    40  // over HTTPS.
    41  type Communicator struct {
    42  	p           Params
    43  	hs          http.Server
    44  	l           net.Listener
    45  	fs          comms.Context
    46  	stopping    chan struct{}
    47  	running     bool
    48  	runningLock sync.RWMutex
    49  	pending     sync.WaitGroup
    50  }
    51  
    52  type guardedListener struct {
    53  	net.Listener
    54  	auth authorizer.Authorizer
    55  }
    56  
    57  func (l guardedListener) Accept() (net.Conn, error) {
    58  	for {
    59  		c, err := l.Listener.Accept()
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		if l.auth.Allow1(c.RemoteAddr()) {
    64  			return c, err
    65  		}
    66  		c.Close()
    67  	}
    68  }
    69  
    70  type listener struct {
    71  	*net.TCPListener
    72  }
    73  
    74  func (l listener) Accept() (net.Conn, error) {
    75  	tc, err := l.AcceptTCP()
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	tc.SetKeepAlive(true)
    80  	tc.SetKeepAlivePeriod(1 * time.Minute)
    81  	tc.SetNoDelay(false)
    82  	return tc, nil
    83  }
    84  
    85  // Params wraps the parameters required to create an https communicator.
    86  type Params struct {
    87  	Listener                    net.Listener        // Where to listen for connections, required.
    88  	Cert, Key                   []byte              // x509 encoded certificate and matching private key, required.
    89  	Streaming                   bool                // Whether to enable streaming communications.
    90  	FrontendConfig              *cpb.FrontendConfig // Configure how the frontend identifies and communicates with clients
    91  	StreamingLifespan           time.Duration       // Maximum time to keep a streaming connection open, defaults to 10 min.
    92  	StreamingCloseTime          time.Duration       // How much of StreamingLifespan to allocate to an orderly stream close, defaults to 30 sec.
    93  	StreamingJitter             time.Duration       // Maximum amount of jitter to add to StreamingLifespan.
    94  	MaxPerClientBatchProcessors uint32              // Maximum number of concurrent processors for messages coming from a single client.
    95  }
    96  
    97  // NewCommunicator creates a Communicator, which listens through l and identifies
    98  // itself using certFile and keyFile.
    99  func NewCommunicator(p Params) (*Communicator, error) {
   100  	if p.StreamingLifespan == 0 {
   101  		p.StreamingLifespan = 10 * time.Minute
   102  	}
   103  	if p.StreamingCloseTime == 0 {
   104  		p.StreamingCloseTime = 30 * time.Second
   105  	}
   106  	if p.MaxPerClientBatchProcessors == 0 {
   107  		p.MaxPerClientBatchProcessors = 10
   108  	}
   109  
   110  	mux := http.NewServeMux()
   111  	h := Communicator{
   112  		p: p,
   113  		hs: http.Server{
   114  			Handler:           mux,
   115  			ReadTimeout:       20 * time.Minute,
   116  			ReadHeaderTimeout: 10 * time.Second,
   117  			WriteTimeout:      20 * time.Minute,
   118  			IdleTimeout:       30 * time.Second,
   119  		},
   120  		stopping: make(chan struct{}),
   121  	}
   122  
   123  	if p.FrontendConfig.GetCleartextHeaderConfig() == nil &&
   124  		p.FrontendConfig.GetCleartextHeaderChecksumConfig() == nil &&
   125  		p.FrontendConfig.GetCleartextXfccConfig() == nil {
   126  		c, err := tls.X509KeyPair(p.Cert, p.Key)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		h.hs.TLSConfig = &tls.Config{
   131  			ClientAuth:   tls.RequestClientCert,
   132  			Certificates: []tls.Certificate{c},
   133  			CipherSuites: []uint16{
   134  				// We may as well allow only the strongest (as far as we can guess)
   135  				// ciphers. Note that TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 is
   136  				// required by the https library.
   137  				tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   138  				tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   139  				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   140  				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
   141  			// Correctly implementing session tickets means sharing and rotating a
   142  			// secret key between servers, with implications if it leaks. Simply
   143  			// disable for the moment.
   144  			SessionTicketsDisabled: true,
   145  			MinVersion:             tls.VersionTLS12,
   146  			NextProtos:             []string{"h2"},
   147  		}
   148  	}
   149  	mux.Handle("/message", messageServer{&h})
   150  	if p.Streaming {
   151  		mux.Handle("/streaming-message", newStreamingMessageServer(&h, p.MaxPerClientBatchProcessors))
   152  	}
   153  	mux.Handle("/files/", fileServer{&h})
   154  
   155  	switch l := h.p.Listener.(type) {
   156  	case *net.TCPListener:
   157  		h.p.Listener = listener{l}
   158  	default:
   159  	}
   160  
   161  	return &h, nil
   162  }
   163  
   164  func (c *Communicator) serve(l net.Listener) {
   165  	err := c.hs.Serve(l)
   166  	log.Errorf("Serving finished with error: %v", err)
   167  }
   168  
   169  func (c *Communicator) Setup(fs comms.Context) error {
   170  	c.fs = fs
   171  	c.p.Listener = guardedListener{
   172  		Listener: c.p.Listener,
   173  		auth:     fs.Authorizer(),
   174  	}
   175  	return nil
   176  }
   177  
   178  func (c *Communicator) Start() error {
   179  	switch {
   180  	case c.p.FrontendConfig.GetCleartextHeaderConfig() != nil,
   181  		c.p.FrontendConfig.GetCleartextHeaderChecksumConfig() != nil,
   182  		c.p.FrontendConfig.GetCleartextXfccConfig() != nil:
   183  		go c.serve(c.p.Listener)
   184  	default:
   185  		go c.serve(tls.NewListener(c.p.Listener, c.hs.TLSConfig))
   186  	}
   187  	c.runningLock.Lock()
   188  	defer c.runningLock.Unlock()
   189  	c.running = true
   190  	return nil
   191  }
   192  
   193  func (c *Communicator) Stop() {
   194  	// The most graceful way to shut down an http.Server is to close the associated listener.
   195  	c.p.Listener.Close()
   196  	c.runningLock.Lock()
   197  	c.running = false
   198  	c.runningLock.Unlock()
   199  	close(c.stopping)
   200  	c.pending.Wait()
   201  }
   202  
   203  // startProcessing returns if we are up and running. If we are up and running,
   204  // it updates the pending operation count to support orderly shutdown.
   205  func (c *Communicator) startProcessing() bool {
   206  	c.runningLock.RLock()
   207  	defer c.runningLock.RUnlock()
   208  	if c.running {
   209  		c.pending.Add(1)
   210  	}
   211  	return c.running
   212  }
   213  
   214  func (c *Communicator) stopProcessing() {
   215  	c.pending.Done()
   216  }