Skip to content

Commit

Permalink
Merge pull request #380 from daschwanden:client-cert-header
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 574421501
  • Loading branch information
copybara-github committed Oct 18, 2023
2 parents 75a76ba + 6ae404f commit 59f41ef
Show file tree
Hide file tree
Showing 17 changed files with 589 additions and 138 deletions.
7 changes: 4 additions & 3 deletions fleetspeak/src/client/comms.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,10 @@ func (c commsContext) ServerInfo() (comms.ServerInfo, error) {
cfg := c.c.config.Configuration()

return comms.ServerInfo{
TrustedCerts: cfg.TrustedCerts,
Servers: cfg.Servers,
Proxy: cfg.Proxy,
TrustedCerts: cfg.TrustedCerts,
Servers: cfg.Servers,
Proxy: cfg.Proxy,
ClientCertificateHeader: cfg.ClientCertificateHeader,
}, nil
}

Expand Down
4 changes: 4 additions & 0 deletions fleetspeak/src/client/comms/comms.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ type ServerInfo struct {
// If non-nil, proxy used for connecting to the server.
// See https://golang.org/pkg/net/http/#Transport.Proxy for details.
Proxy *url.URL

// If non-empty, populated with the header to use for the
// client certificate header
ClientCertificateHeader string
}

// A Context describes the view of the Fleetspeak client provided to a Communicator.
Expand Down
5 changes: 5 additions & 0 deletions fleetspeak/src/client/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ type Configuration struct {
// If non-nil, proxy used for connecting to the server.
// See https://golang.org/pkg/net/http/#Transport.Proxy for details.
Proxy *url.URL

// If set, the server will validate the client certificate from the request header.
// This should be used if TLS is terminated at the load balancer and client certificates
// can be passed upstream to the fleetspeak server as an http header.
ClientCertificateHeader string
}

// PersistenceHandler manages client's configuration storage.
Expand Down
11 changes: 6 additions & 5 deletions fleetspeak/src/client/generic/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ func MakeConfiguration(cfg *gpb.Config) (config.Configuration, error) {
}

return config.Configuration{
TrustedCerts: trustedCerts,
Servers: cfg.Server,
PersistenceHandler: ph,
ClientLabels: labels,
Proxy: proxy,
TrustedCerts: trustedCerts,
Servers: cfg.Server,
PersistenceHandler: ph,
ClientLabels: labels,
Proxy: proxy,
ClientCertificateHeader: cfg.ClientCertificateHeader,
}, nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ message Config {
// The format is a URL.
// See https://golang.org/pkg/net/http/#Transport.Proxy for details.
string proxy = 7;

// The name of the HTTP header that the client uses to pass its certificate to
// the server frontend for identification. Required only if the server
// frontend is configured to use https_header_checksum_config.
string client_certificate_header = 8;
}

message FilesystemHandler {
Expand Down
10 changes: 5 additions & 5 deletions fleetspeak/src/client/https/https.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ const (
closeWaitThreshold = 30 * time.Second // Matches IdleTimeout in server/https.
)

func makeTransport(cctx comms.Context, dc func(ctx context.Context, network, addr string) (net.Conn, error)) (common.ClientID, *http.Transport, error) {
func makeTransport(cctx comms.Context, dc func(ctx context.Context, network, addr string) (net.Conn, error)) (common.ClientID, *http.Transport, []byte, error) {
ci, err := cctx.CurrentIdentity()
if err != nil {
return common.ClientID{}, nil, err
return common.ClientID{}, nil, nil, err
}
si, err := cctx.ServerInfo()
if err != nil {
return common.ClientID{}, nil, err
return common.ClientID{}, nil, nil, err
}

cv := func(_ [][]byte, chains [][]*x509.Certificate) error {
Expand All @@ -69,7 +69,7 @@ func makeTransport(cctx comms.Context, dc func(ctx context.Context, network, add
}
certBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, ci.Public, ci.Private)
if err != nil {
return common.ClientID{}, nil, fmt.Errorf("unable to configure communicator, could not create client cert: %v", err)
return common.ClientID{}, nil, nil, fmt.Errorf("unable to configure communicator, could not create client cert: %v", err)
}

if dc == nil {
Expand Down Expand Up @@ -112,7 +112,7 @@ func makeTransport(cctx comms.Context, dc func(ctx context.Context, network, add
DialContext: dc,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}, nil
}, certBytes, nil
}

// jitter adds up to 50% random jitter, and converts to time.Duration.
Expand Down
2 changes: 1 addition & 1 deletion fleetspeak/src/client/https/polling.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *Communicator) Setup(cl comms.Context) error {
}

func (c *Communicator) configure() error {
id, tr, err := makeTransport(c.cctx, c.DialContext)
id, tr, _, err := makeTransport(c.cctx, c.DialContext)
if err != nil {
return err
}
Expand Down
15 changes: 14 additions & 1 deletion fleetspeak/src/client/https/streaming.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bufio"
"context"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -57,6 +58,11 @@ type StreamingCommunicator struct {

// 1 hour watchdog for server communication attempts.
wd *watchdog.Watchdog

// header name for the client certificate
clientCertificateHeader string

certBytes []byte
}

func (c *StreamingCommunicator) Setup(cl comms.Context) error {
Expand Down Expand Up @@ -96,7 +102,7 @@ func (c *StreamingCommunicator) GetFileIfModified(ctx context.Context, service,
}

func (c *StreamingCommunicator) configure() error {
id, tr, err := makeTransport(c.cctx, c.DialContext)
id, tr, certBytes, err := makeTransport(c.cctx, c.DialContext)
if err != nil {
return err
}
Expand All @@ -114,6 +120,8 @@ func (c *StreamingCommunicator) configure() error {
Transport: tr,
Timeout: 15 * time.Minute,
}
c.clientCertificateHeader = si.ClientCertificateHeader
c.certBytes = certBytes
return nil
}

Expand Down Expand Up @@ -258,6 +266,11 @@ func (c *StreamingCommunicator) connect(ctx context.Context, host string, maxLif
req.ContentLength = -1
req.Close = true
req.Header.Set("Expect", "100-continue")
if c.clientCertificateHeader != "" {
bc := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.certBytes})
cc := url.PathEscape(string(bc))
req.Header.Set(c.clientCertificateHeader, cc)
}
req = req.WithContext(ret.ctx)

// If ctx terminates during the initial Do, we want ret.ctx to end, but
Expand Down
Loading

0 comments on commit 59f41ef

Please sign in to comment.