mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-24 17:04:42 +01:00
feat(client): add mTLS config (#665)
* feat: add mtls config to client * feat: add mtls config to client * Rework client tls configuration * Rebase (#3) * chore(deps): bump codecov/codecov-action from 3.1.6 to 4.0.1 (#671) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3.1.6 to 4.0.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3.1.6...v4.0.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(client): enhance HTTP client configuration with proxy support (#668) * feat: enhance HTTP client configuration with proxy support - Add `ProxyURL` field to `Config` struct with YAML tag - Implement proxy URL parsing and setting in `getHTTPClient` method - Add test case for `getHTTPClient` method with custom proxy URL setting - Include `net/url` package in both `config.go` and `config_test.go` files Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * docs: enhance README with Proxy and OAuth2 Docs - Remove empty lines from README.md - Add documentation for proxy configuration in client examples - Include YAML examples for client using a proxy, custom DNS resolver, OAuth2, and identity-aware proxy configurations in README.md Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * docs: add proxy client Signed-off-by: appleboy <appleboy.tw@gmail.com> * Update client/config.go * Update README.md * Update client/config_test.go --------- Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Signed-off-by: appleboy <appleboy.tw@gmail.com> Co-authored-by: TwiN <twin@linux.com> * chore: Update Go to 1.21 * chore(deps): bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0 (#658) chore(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/gofiber/fiber/v2 from 2.49.2 to 2.52.1 (#682) Bumps [github.com/gofiber/fiber/v2](https://github.com/gofiber/fiber) from 2.49.2 to 2.52.1. - [Release notes](https://github.com/gofiber/fiber/releases) - [Commits](https://github.com/gofiber/fiber/compare/v2.49.2...v2.52.1) --- updated-dependencies: - dependency-name: github.com/gofiber/fiber/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs(alerting): Fix wrong gitlab terminology (alert key vs. PAT) (#694) Fix wrong term (alert key vs. PAT) * chore(deps): bump github.com/TwiN/deepmerge from 0.2.0 to 0.2.1 (#684) * chore(deps): bump github.com/TwiN/deepmerge from 0.2.0 to 0.2.1 Bumps [github.com/TwiN/deepmerge](https://github.com/TwiN/deepmerge) from 0.2.0 to 0.2.1. - [Release notes](https://github.com/TwiN/deepmerge/releases) - [Commits](https://github.com/TwiN/deepmerge/compare/v0.2.0...v0.2.1) --- updated-dependencies: - dependency-name: github.com/TwiN/deepmerge dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * build: Add `go mod tidy` in Dockerfile * ci: Update Go to 1.20 * Update go.mod * Update test.yml --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: TwiN <twin@linux.com> * chore(deps): bump golang.org/x/oauth2 from 0.13.0 to 0.18.0 (#701) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.13.0 to 0.18.0. - [Commits](https://github.com/golang/oauth2/compare/v0.13.0...v0.18.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add mtls config to client feat: add mtls config to client Rework client tls configuration --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Signed-off-by: appleboy <appleboy.tw@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-authored-by: TwiN <twin@linux.com> Co-authored-by: Salim B <git@salim.space> * Rebase (#4) * chore(deps): bump codecov/codecov-action from 3.1.6 to 4.0.1 (#671) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3.1.6 to 4.0.1. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3.1.6...v4.0.1) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(client): enhance HTTP client configuration with proxy support (#668) * feat: enhance HTTP client configuration with proxy support - Add `ProxyURL` field to `Config` struct with YAML tag - Implement proxy URL parsing and setting in `getHTTPClient` method - Add test case for `getHTTPClient` method with custom proxy URL setting - Include `net/url` package in both `config.go` and `config_test.go` files Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * docs: enhance README with Proxy and OAuth2 Docs - Remove empty lines from README.md - Add documentation for proxy configuration in client examples - Include YAML examples for client using a proxy, custom DNS resolver, OAuth2, and identity-aware proxy configurations in README.md Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> * docs: add proxy client Signed-off-by: appleboy <appleboy.tw@gmail.com> * Update client/config.go * Update README.md * Update client/config_test.go --------- Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Signed-off-by: appleboy <appleboy.tw@gmail.com> Co-authored-by: TwiN <twin@linux.com> * chore: Update Go to 1.21 * chore(deps): bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0 (#658) chore(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump github.com/gofiber/fiber/v2 from 2.49.2 to 2.52.1 (#682) Bumps [github.com/gofiber/fiber/v2](https://github.com/gofiber/fiber) from 2.49.2 to 2.52.1. - [Release notes](https://github.com/gofiber/fiber/releases) - [Commits](https://github.com/gofiber/fiber/compare/v2.49.2...v2.52.1) --- updated-dependencies: - dependency-name: github.com/gofiber/fiber/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * docs(alerting): Fix wrong gitlab terminology (alert key vs. PAT) (#694) Fix wrong term (alert key vs. PAT) * chore(deps): bump github.com/TwiN/deepmerge from 0.2.0 to 0.2.1 (#684) * chore(deps): bump github.com/TwiN/deepmerge from 0.2.0 to 0.2.1 Bumps [github.com/TwiN/deepmerge](https://github.com/TwiN/deepmerge) from 0.2.0 to 0.2.1. - [Release notes](https://github.com/TwiN/deepmerge/releases) - [Commits](https://github.com/TwiN/deepmerge/compare/v0.2.0...v0.2.1) --- updated-dependencies: - dependency-name: github.com/TwiN/deepmerge dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * build: Add `go mod tidy` in Dockerfile * ci: Update Go to 1.20 * Update go.mod * Update test.yml --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: TwiN <twin@linux.com> * chore(deps): bump golang.org/x/oauth2 from 0.13.0 to 0.18.0 (#701) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.13.0 to 0.18.0. - [Commits](https://github.com/golang/oauth2/compare/v0.13.0...v0.18.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add mtls config to client * feat: add mtls config to client * Rework client tls configuration --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Signed-off-by: appleboy <appleboy.tw@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-authored-by: TwiN <twin@linux.com> Co-authored-by: Salim B <git@salim.space> * Rebase (#6) * feat(tls): add mtls config to client (#189) --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com> Signed-off-by: appleboy <appleboy.tw@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-authored-by: TwiN <twin@linux.com> Co-authored-by: Salim B <git@salim.space>
This commit is contained in:
parent
f54c45e20e
commit
f93cebe715
33
README.md
33
README.md
@ -417,8 +417,12 @@ the client used to send the request.
|
|||||||
| `client.proxy-url` | The URL of the proxy to use for the client | `""` |
|
| `client.proxy-url` | The URL of the proxy to use for the client | `""` |
|
||||||
| `client.identity-aware-proxy` | Google Identity-Aware-Proxy client configuration. | `{}` |
|
| `client.identity-aware-proxy` | Google Identity-Aware-Proxy client configuration. | `{}` |
|
||||||
| `client.identity-aware-proxy.audience` | The Identity-Aware-Proxy audience. (client-id of the IAP oauth2 credential) | required `""` |
|
| `client.identity-aware-proxy.audience` | The Identity-Aware-Proxy audience. (client-id of the IAP oauth2 credential) | required `""` |
|
||||||
|
| `client.tls.certificate-file` | Path to a client certificate (in PEM format) for mTLS configurations. | `""` |
|
||||||
|
| `client.tls.private-key-file` | Path to a client private key (in PEM format) for mTLS configurations. | `""` |
|
||||||
|
| `client.tls.renegotiation` | Type of renegotiation support to provide. (`never`, `freely`, `once`). | `"never"` |
|
||||||
| `client.network` | The network to use for ICMP endpoint client (`ip`, `ip4` or `ip6`). | `"ip"` |
|
| `client.network` | The network to use for ICMP endpoint client (`ip`, `ip4` or `ip6`). | `"ip"` |
|
||||||
|
|
||||||
|
|
||||||
> 📝 Some of these parameters are ignored based on the type of endpoint. For instance, there's no certificate involved
|
> 📝 Some of these parameters are ignored based on the type of endpoint. For instance, there's no certificate involved
|
||||||
> in ICMP requests (ping), therefore, setting `client.insecure` to `true` for an endpoint of that type will not do anything.
|
> in ICMP requests (ping), therefore, setting `client.insecure` to `true` for an endpoint of that type will not do anything.
|
||||||
|
|
||||||
@ -490,6 +494,22 @@ endpoints:
|
|||||||
|
|
||||||
> 📝 Note that Gatus will use the [gcloud default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) within its environment to generate the token.
|
> 📝 Note that Gatus will use the [gcloud default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) within its environment to generate the token.
|
||||||
|
|
||||||
|
This example shows you how you cna use the `client.tls` configuration to perform an mTLS query to a backend API:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
endpoints:
|
||||||
|
- name: website
|
||||||
|
url: "https://your.mtls.protected.app/health"
|
||||||
|
client:
|
||||||
|
tls:
|
||||||
|
certificate-file: /path/to/user_cert.pem
|
||||||
|
private-key-file: /path/to/user_key.pem
|
||||||
|
renegotiation: once
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
```
|
||||||
|
|
||||||
|
> 📝 Note that if running in a container, you must volume mount the certificate and key into the container.
|
||||||
|
|
||||||
### Alerting
|
### Alerting
|
||||||
Gatus supports multiple alerting providers, such as Slack and PagerDuty, and supports different alerts for each
|
Gatus supports multiple alerting providers, such as Slack and PagerDuty, and supports different alerts for each
|
||||||
@ -2059,6 +2079,19 @@ endpoints:
|
|||||||
- "[STATUS] == 200"
|
- "[STATUS] == 200"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Proxy client configuration
|
||||||
|
|
||||||
|
You can configure a proxy for the client to use by setting the `proxy-url` parameter in the client configuration.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
endpoints:
|
||||||
|
- name: website
|
||||||
|
url: "https://twin.sh/health"
|
||||||
|
client:
|
||||||
|
proxy-url: http://proxy.example.com:8080
|
||||||
|
conditions:
|
||||||
|
- "[STATUS] == 200"
|
||||||
|
```
|
||||||
|
|
||||||
### How to fix 431 Request Header Fields Too Large error
|
### How to fix 431 Request Header Fields Too Large error
|
||||||
Depending on where your environment is deployed and what kind of middleware or reverse proxy sits in front of Gatus,
|
Depending on where your environment is deployed and what kind of middleware or reverse proxy sits in front of Gatus,
|
||||||
|
@ -2,6 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
@ -290,3 +291,46 @@ func TestQueryWebSocket(t *testing.T) {
|
|||||||
t.Error("expected an error due to the target not being websocket-friendly")
|
t.Error("expected an error due to the target not being websocket-friendly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTlsRenegotiation(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg TLSConfig
|
||||||
|
expectedConfig tls.RenegotiationSupport
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default",
|
||||||
|
cfg: TLSConfig{CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"},
|
||||||
|
expectedConfig: tls.RenegotiateNever,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "never",
|
||||||
|
cfg: TLSConfig{RenegotiationSupport: "never", CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"},
|
||||||
|
expectedConfig: tls.RenegotiateNever,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "once",
|
||||||
|
cfg: TLSConfig{RenegotiationSupport: "once", CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"},
|
||||||
|
expectedConfig: tls.RenegotiateOnceAsClient,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "freely",
|
||||||
|
cfg: TLSConfig{RenegotiationSupport: "freely", CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"},
|
||||||
|
expectedConfig: tls.RenegotiateFreelyAsClient,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not-valid-and-broken",
|
||||||
|
cfg: TLSConfig{RenegotiationSupport: "invalid", CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"},
|
||||||
|
expectedConfig: tls.RenegotiateNever,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
tls := &tls.Config{}
|
||||||
|
tlsConfig := configureTLS(tls, test.cfg)
|
||||||
|
if tlsConfig.Renegotiation != test.expectedConfig {
|
||||||
|
t.Errorf("expected tls renegotiation to be %v, but got %v", test.expectedConfig, tls.Renegotiation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ var (
|
|||||||
ErrInvalidDNSResolverPort = errors.New("invalid DNS resolver port")
|
ErrInvalidDNSResolverPort = errors.New("invalid DNS resolver port")
|
||||||
ErrInvalidClientOAuth2Config = errors.New("invalid oauth2 configuration: must define all fields for client credentials flow (token-url, client-id, client-secret, scopes)")
|
ErrInvalidClientOAuth2Config = errors.New("invalid oauth2 configuration: must define all fields for client credentials flow (token-url, client-id, client-secret, scopes)")
|
||||||
ErrInvalidClientIAPConfig = errors.New("invalid Identity-Aware-Proxy configuration: must define all fields for Google Identity-Aware-Proxy programmatic authentication (audience)")
|
ErrInvalidClientIAPConfig = errors.New("invalid Identity-Aware-Proxy configuration: must define all fields for Google Identity-Aware-Proxy programmatic authentication (audience)")
|
||||||
|
ErrInvalidClientTLSConfig = errors.New("invalid TLS configuration: certificate-file and private-key-file must be specified")
|
||||||
|
|
||||||
defaultConfig = Config{
|
defaultConfig = Config{
|
||||||
Insecure: false,
|
Insecure: false,
|
||||||
@ -72,6 +73,9 @@ type Config struct {
|
|||||||
|
|
||||||
// Network (ip, ip4 or ip6) for the ICMP client
|
// Network (ip, ip4 or ip6) for the ICMP client
|
||||||
Network string `yaml:"network"`
|
Network string `yaml:"network"`
|
||||||
|
|
||||||
|
// TLS configuration (optional)
|
||||||
|
TLS *TLSConfig `yaml:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSResolverConfig is the parsed configuration from the DNSResolver config string.
|
// DNSResolverConfig is the parsed configuration from the DNSResolver config string.
|
||||||
@ -94,6 +98,17 @@ type IAPConfig struct {
|
|||||||
Audience string `yaml:"audience"` // e.g. "toto.apps.googleusercontent.com"
|
Audience string `yaml:"audience"` // e.g. "toto.apps.googleusercontent.com"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig is the configuration for mTLS configurations
|
||||||
|
type TLSConfig struct {
|
||||||
|
// CertificateFile is the public certificate for TLS in PEM format.
|
||||||
|
CertificateFile string `yaml:"certificate-file,omitempty"`
|
||||||
|
|
||||||
|
// PrivateKeyFile is the private key file for TLS in PEM format.
|
||||||
|
PrivateKeyFile string `yaml:"private-key-file,omitempty"`
|
||||||
|
|
||||||
|
RenegotiationSupport string `yaml:"renegotiation,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateAndSetDefaults validates the client configuration and sets the default values if necessary
|
// ValidateAndSetDefaults validates the client configuration and sets the default values if necessary
|
||||||
func (c *Config) ValidateAndSetDefaults() error {
|
func (c *Config) ValidateAndSetDefaults() error {
|
||||||
if c.Timeout < time.Millisecond {
|
if c.Timeout < time.Millisecond {
|
||||||
@ -111,6 +126,11 @@ func (c *Config) ValidateAndSetDefaults() error {
|
|||||||
if c.HasIAPConfig() && !c.IAPConfig.isValid() {
|
if c.HasIAPConfig() && !c.IAPConfig.isValid() {
|
||||||
return ErrInvalidClientIAPConfig
|
return ErrInvalidClientIAPConfig
|
||||||
}
|
}
|
||||||
|
if c.HasTlsConfig() {
|
||||||
|
if err := c.TLS.isValid(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +176,11 @@ func (c *Config) HasIAPConfig() bool {
|
|||||||
return c.IAPConfig != nil
|
return c.IAPConfig != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasTlsConfig returns true if the client has client certificate parameters
|
||||||
|
func (c *Config) HasTlsConfig() bool {
|
||||||
|
return c.TLS != nil && len(c.TLS.CertificateFile) > 0 && len(c.TLS.PrivateKeyFile) > 0
|
||||||
|
}
|
||||||
|
|
||||||
// isValid() returns true if the IAP configuration is valid
|
// isValid() returns true if the IAP configuration is valid
|
||||||
func (c *IAPConfig) isValid() bool {
|
func (c *IAPConfig) isValid() bool {
|
||||||
return len(c.Audience) > 0
|
return len(c.Audience) > 0
|
||||||
@ -166,8 +191,26 @@ func (c *OAuth2Config) isValid() bool {
|
|||||||
return len(c.TokenURL) > 0 && len(c.ClientID) > 0 && len(c.ClientSecret) > 0 && len(c.Scopes) > 0
|
return len(c.TokenURL) > 0 && len(c.ClientID) > 0 && len(c.ClientSecret) > 0 && len(c.Scopes) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValid() returns nil if the client tls certificates are valid, otherwise returns an error
|
||||||
|
func (t *TLSConfig) isValid() error {
|
||||||
|
if len(t.CertificateFile) > 0 && len(t.PrivateKeyFile) > 0 {
|
||||||
|
_, err := tls.LoadX509KeyPair(t.CertificateFile, t.PrivateKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ErrInvalidClientTLSConfig
|
||||||
|
}
|
||||||
|
|
||||||
// GetHTTPClient return an HTTP client matching the Config's parameters.
|
// GetHTTPClient return an HTTP client matching the Config's parameters.
|
||||||
func (c *Config) getHTTPClient() *http.Client {
|
func (c *Config) getHTTPClient() *http.Client {
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
InsecureSkipVerify: c.Insecure,
|
||||||
|
}
|
||||||
|
if c.HasTlsConfig() && c.TLS.isValid() == nil {
|
||||||
|
tlsConfig = configureTLS(tlsConfig, *c.TLS)
|
||||||
|
}
|
||||||
if c.httpClient == nil {
|
if c.httpClient == nil {
|
||||||
c.httpClient = &http.Client{
|
c.httpClient = &http.Client{
|
||||||
Timeout: c.Timeout,
|
Timeout: c.Timeout,
|
||||||
@ -175,9 +218,7 @@ func (c *Config) getHTTPClient() *http.Client {
|
|||||||
MaxIdleConns: 100,
|
MaxIdleConns: 100,
|
||||||
MaxIdleConnsPerHost: 20,
|
MaxIdleConnsPerHost: 20,
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: tlsConfig,
|
||||||
InsecureSkipVerify: c.Insecure,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
if c.IgnoreRedirect {
|
if c.IgnoreRedirect {
|
||||||
@ -281,3 +322,23 @@ func configureOAuth2(httpClient *http.Client, c OAuth2Config) *http.Client {
|
|||||||
client.Timeout = httpClient.Timeout
|
client.Timeout = httpClient.Timeout
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configureTLS returns a TLS Config that will enable mTLS
|
||||||
|
func configureTLS(tlsConfig *tls.Config, c TLSConfig) *tls.Config {
|
||||||
|
clientTLSCert, err := tls.LoadX509KeyPair(c.CertificateFile, c.PrivateKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tlsConfig.Certificates = []tls.Certificate{clientTLSCert}
|
||||||
|
tlsConfig.Renegotiation = tls.RenegotiateNever
|
||||||
|
|
||||||
|
renegotionSupport := map[string]tls.RenegotiationSupport{
|
||||||
|
"once": tls.RenegotiateOnceAsClient,
|
||||||
|
"freely": tls.RenegotiateFreelyAsClient,
|
||||||
|
"never": tls.RenegotiateNever,
|
||||||
|
}
|
||||||
|
if val, ok := renegotionSupport[c.RenegotiationSupport]; ok {
|
||||||
|
tlsConfig.Renegotiation = val
|
||||||
|
}
|
||||||
|
return tlsConfig
|
||||||
|
}
|
||||||
|
@ -106,3 +106,66 @@ func TestConfig_getHTTPClient_withCustomProxyURL(t *testing.T) {
|
|||||||
t.Errorf("expected Config.ProxyURL to set the HTTP client's proxy to %s", proxyURL)
|
t.Errorf("expected Config.ProxyURL to set the HTTP client's proxy to %s", proxyURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfig_TlsIsValid(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
cfg *Config
|
||||||
|
expectedErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "good-tls-config",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/cert.key"}},
|
||||||
|
expectedErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-certificate-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "doesnotexist", PrivateKeyFile: "../testdata/cert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-certificate-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/badcert.pem", PrivateKeyFile: "../testdata/cert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-certificate-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "", PrivateKeyFile: "../testdata/cert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing-private-key-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "doesnotexist"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-private-key-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/cert.pem", PrivateKeyFile: ""}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-private-key-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/cert.pem", PrivateKeyFile: "../testdata/badcert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad-certificate-and-private-key-file",
|
||||||
|
cfg: &Config{TLS: &TLSConfig{CertificateFile: "../testdata/badcert.pem", PrivateKeyFile: "../testdata/badcert.key"}},
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
err := test.cfg.TLS.isValid()
|
||||||
|
if (err != nil) != test.expectedErr {
|
||||||
|
t.Errorf("expected the existence of an error to be %v, got %v", test.expectedErr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !test.expectedErr {
|
||||||
|
if test.cfg.TLS.isValid() != nil {
|
||||||
|
t.Error("cfg.TLS.isValid() returned an error even though no error was expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user