Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggregator: Add support for client certificates and extra header #424

Merged
merged 8 commits into from
Aug 17, 2023
120 changes: 96 additions & 24 deletions cmd/csaf_aggregator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (

"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/csaf-poc/csaf_distribution/v2/csaf"
"github.com/csaf-poc/csaf_distribution/v2/internal/certs"
"github.com/csaf-poc/csaf_distribution/v2/internal/filter"
"github.com/csaf-poc/csaf_distribution/v2/internal/options"
"github.com/csaf-poc/csaf_distribution/v2/util"
Expand Down Expand Up @@ -52,6 +53,15 @@ type provider struct {

// IgnorePattern is a list of patterns of advisory URLs to be ignored.
IgnorePattern []string `toml:"ignorepattern"`

// ExtraHeader adds extra HTTP header fields to client
ExtraHeader http.Header `toml:"header"`

ClientCert *string `toml:"client_cert"`
ClientKey *string `toml:"client_key"`
ClientPassphrase *string `toml:"client_passphrase"`

clientCerts []tls.Certificate
ignorePattern filter.PatternMatcher
}

Expand All @@ -74,6 +84,10 @@ type config struct {
Passphrase *string `toml:"passphrase"`
AllowSingleProvider bool `toml:"allow_single_provider"`

ClientCert *string `toml:"client_cert"`
ClientKey *string `toml:"client_key"`
ClientPassphrase *string `toml:"client_passphrase"`

// LockFile tries to lock to a given file.
LockFile *string `toml:"lock_file"`

Expand All @@ -97,13 +111,18 @@ type config struct {

// IgnorePattern is a list of patterns of advisory URLs to be ignored.
IgnorePattern []string `toml:"ignorepattern"`
ignorePattern filter.PatternMatcher

// ExtraHeader adds extra HTTP header fields to client
ExtraHeader http.Header `toml:"header"`

Config string `short:"c" long:"config" description:"Path to config TOML file" value-name:"TOML-FILE" toml:"-"`

keyMu sync.Mutex
key *crypto.Key
keyErr error

clientCerts []tls.Certificate
ignorePattern filter.PatternMatcher
}

// configPaths are the potential file locations of the config file.
Expand Down Expand Up @@ -217,20 +236,44 @@ func (c *config) privateOpenPGPKey() (*crypto.Key, error) {
func (c *config) httpClient(p *provider) util.Client {

hClient := http.Client{}

var tlsConfig tls.Config
if p.Insecure != nil && *p.Insecure || c.Insecure != nil && *c.Insecure {
hClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}
tlsConfig.InsecureSkipVerify = true
}

var client util.Client
// Use client certs if needed.
switch {
// Provider has precedence over global.
case len(p.clientCerts) != 0:
tlsConfig.Certificates = p.clientCerts
case len(c.clientCerts) != 0:
tlsConfig.Certificates = c.clientCerts
}

hClient.Transport = &http.Transport{
TLSClientConfig: &tlsConfig,
}

client := util.Client(&hClient)

// Add extra headers.
switch {
// Provider has precedence over global.
case len(p.ExtraHeader) > 0:
client = &util.HeaderClient{
Client: client,
Header: p.ExtraHeader,
}
case len(c.ExtraHeader) > 0:
client = &util.HeaderClient{
Client: client,
Header: c.ExtraHeader,
}
}

if c.Verbose {
client = &util.LoggingClient{Client: &hClient}
} else {
client = &hClient
client = &util.LoggingClient{Client: client}
}

if p.Rate == nil && c.Rate == nil {
Expand Down Expand Up @@ -325,7 +368,7 @@ func (c *config) setDefaults() {
func (p *provider) compileIgnorePatterns() error {
pm, err := filter.NewPatternMatcher(p.IgnorePattern)
if err != nil {
return err
return fmt.Errorf("invalid ignore patterns for %q: %w", p.Name, err)
}
p.ignorePattern = pm
return nil
Expand All @@ -342,30 +385,59 @@ func (c *config) compileIgnorePatterns() error {
// Compile the patterns of the providers.
for _, p := range c.Providers {
if err := p.compileIgnorePatterns(); err != nil {
return fmt.Errorf("invalid ignore patterns for %q: %w", p.Name, err)
return err
}
}
return nil
}

// prepare prepares internal state of a loaded configuration.
func (c *config) prepare() error {

if len(c.Providers) == 0 {
return errors.New("no providers given in configuration")
// prepareCertificates loads the provider specific client side certificates
// used by the HTTP client.
func (p *provider) prepareCertificates() error {
cert, err := certs.LoadCertificate(
p.ClientCert, p.ClientKey, p.ClientPassphrase)
if err != nil {
return fmt.Errorf("invalid certificates for %q: %w", p.Name, err)
}
p.clientCerts = cert
return nil
}

if err := c.compileIgnorePatterns(); err != nil {
// prepareCertificates loads the client side certificates used by the HTTP client.
func (c *config) prepareCertificates() error {
// Global certificates
cert, err := certs.LoadCertificate(
c.ClientCert, c.ClientKey, c.ClientPassphrase)
if err != nil {
return err
}

if err := c.Aggregator.Validate(); err != nil {
return err
c.clientCerts = cert
// Provider certificates
for _, p := range c.Providers {
if err := p.prepareCertificates(); err != nil {
return err
}
}
return nil
}

if err := c.checkProviders(); err != nil {
return err
// prepare prepares internal state of a loaded configuration.
func (c *config) prepare() error {

if len(c.Providers) == 0 {
return errors.New("no providers given in configuration")
}

return c.checkMirror()
for _, prepare := range []func() error{
c.prepareCertificates,
c.compileIgnorePatterns,
c.Aggregator.Validate,
c.checkProviders,
c.checkMirror,
} {
if err := prepare(); err != nil {
return err
}
}
return nil
}
12 changes: 12 additions & 0 deletions docs/csaf_aggregator.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ interim_years // limiting the years for which interim documents are se
verbose // print more diagnostic output, e.g. https requests (default false)
allow_single_provider // debugging option (default false)
ignorepattern // patterns of advisory URLs to be ignored
client_cert // path to client certificate to access access-protected advisories
client_key // path to client key to access access-protected advisories
client_passphrase // client passphrase to access access-protected advisories
header // adds extra HTTP header fields to the client
```

Next we have two TOML _tables_:
Expand Down Expand Up @@ -125,6 +129,10 @@ update_interval
create_service_document
categories
ignorepattern
client_cert
client_key
client_passphrase
header
```

Where valid `name` and `domain` settings are required.
Expand Down Expand Up @@ -196,6 +204,10 @@ insecure = true
# rate = 1.2
# insecure = true
write_indices = true
client_cert = "./../devca1/testclient1.crt"
client_key = "./../devca1/testclient1-key.pem"
# client_passphrase =
# header =

[[providers]]
name = "local-dev-provider3"
Expand Down
4 changes: 4 additions & 0 deletions docs/examples/aggregator.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ insecure = true
# rate = 1.2
# insecure = true
write_indices = true
client_cert = "./../devca1/testclient1.crt"
client_key = "./../devca1/testclient1-key.pem"
# client_passphrase =
# header =

[[providers]]
name = "local-dev-provider3"
Expand Down
Loading