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>
This commit is contained in:
Bo-Yi Wu 2024-02-15 10:43:57 +08:00 committed by GitHub
parent 3269e96f49
commit 408a46f2af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 2 deletions

View File

@ -104,6 +104,7 @@ Have any feedback or questions? [Create a discussion](https://github.com/TwiN/ga
- [Exposing Gatus on a custom port](#exposing-gatus-on-a-custom-port) - [Exposing Gatus on a custom port](#exposing-gatus-on-a-custom-port)
- [Configuring a startup delay](#configuring-a-startup-delay) - [Configuring a startup delay](#configuring-a-startup-delay)
- [Keeping your configuration small](#keeping-your-configuration-small) - [Keeping your configuration small](#keeping-your-configuration-small)
- [Proxy client configuration](#proxy-client-configuration)
- [Badges](#badges) - [Badges](#badges)
- [Uptime](#uptime) - [Uptime](#uptime)
- [Health](#health) - [Health](#health)
@ -132,9 +133,9 @@ if no traffic makes it to your applications. This puts you in a situation where
that will notify you about the degradation of your services rather than you reassuring them that you're working on that will notify you about the degradation of your services rather than you reassuring them that you're working on
fixing the issue before they even know about it. fixing the issue before they even know about it.
## Features ## Features
The main features of Gatus are: The main features of Gatus are:
- **Highly flexible health check conditions**: While checking the response status may be enough for some use cases, Gatus goes much further and allows you to add conditions on the response time, the response body and even the IP address. - **Highly flexible health check conditions**: While checking the response status may be enough for some use cases, Gatus goes much further and allows you to add conditions on the response time, the response body and even the IP address.
- **Ability to use Gatus for user acceptance tests**: Thanks to the point above, you can leverage this application to create automated user acceptance tests. - **Ability to use Gatus for user acceptance tests**: Thanks to the point above, you can leverage this application to create automated user acceptance tests.
- **Very easy to configure**: Not only is the configuration designed to be as readable as possible, it's also extremely easy to add a new service or a new endpoint to monitor. - **Very easy to configure**: Not only is the configuration designed to be as readable as possible, it's also extremely easy to add a new service or a new endpoint to monitor.
@ -146,7 +147,6 @@ The main features of Gatus are:
![Gatus dashboard conditions](.github/assets/dashboard-conditions.png) ![Gatus dashboard conditions](.github/assets/dashboard-conditions.png)
## Usage ## Usage
<details> <details>
@ -361,6 +361,7 @@ the client used to send the request.
| `client.oauth2.client-id` | The client id which should be used for the `Client credentials flow` | required `""` | | `client.oauth2.client-id` | The client id which should be used for the `Client credentials flow` | required `""` |
| `client.oauth2.client-secret` | The client secret which should be used for the `Client credentials flow` | required `""` | | `client.oauth2.client-secret` | The client secret which should be used for the `Client credentials flow` | required `""` |
| `client.oauth2.scopes[]` | A list of `scopes` which should be used for the `Client credentials flow`. | required `[""]` | | `client.oauth2.scopes[]` | A list of `scopes` which should be used for the `Client credentials flow`. | required `[""]` |
| `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.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"` |
@ -369,15 +370,18 @@ the client used to send the request.
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.
This default configuration is as follows: This default configuration is as follows:
```yaml ```yaml
client: client:
insecure: false insecure: false
ignore-redirect: false ignore-redirect: false
timeout: 10s timeout: 10s
``` ```
Note that this configuration is only available under `endpoints[]`, `alerting.mattermost` and `alerting.custom`. Note that this configuration is only available under `endpoints[]`, `alerting.mattermost` and `alerting.custom`.
Here's an example with the client configuration under `endpoints[]`: Here's an example with the client configuration under `endpoints[]`:
```yaml ```yaml
endpoints: endpoints:
- name: website - name: website
@ -391,6 +395,7 @@ endpoints:
``` ```
This example shows how you can specify a custom DNS resolver: This example shows how you can specify a custom DNS resolver:
```yaml ```yaml
endpoints: endpoints:
- name: with-custom-dns-resolver - name: with-custom-dns-resolver
@ -402,6 +407,7 @@ endpoints:
``` ```
This example shows how you can use the `client.oauth2` configuration to query a backend API with `Bearer token`: This example shows how you can use the `client.oauth2` configuration to query a backend API with `Bearer token`:
```yaml ```yaml
endpoints: endpoints:
- name: with-custom-oauth2 - name: with-custom-oauth2
@ -417,6 +423,7 @@ endpoints:
``` ```
This example shows how you can use the `client.identity-aware-proxy` configuration to query a backend API with `Bearer token` using Google Identity-Aware-Proxy: This example shows how you can use the `client.identity-aware-proxy` configuration to query a backend API with `Bearer token` using Google Identity-Aware-Proxy:
```yaml ```yaml
endpoints: endpoints:
- name: with-custom-iap - name: with-custom-iap
@ -427,6 +434,7 @@ endpoints:
conditions: conditions:
- "[STATUS] == 200" - "[STATUS] == 200"
``` ```
> 📝 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.
### Alerting ### Alerting
@ -1909,6 +1917,19 @@ endpoints:
``` ```
</details> </details>
### 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,

View File

@ -7,6 +7,7 @@ import (
"log" "log"
"net" "net"
"net/http" "net/http"
"net/url"
"regexp" "regexp"
"strconv" "strconv"
"time" "time"
@ -42,6 +43,9 @@ func GetDefaultConfig() *Config {
// Config is the configuration for clients // Config is the configuration for clients
type Config struct { type Config struct {
// ProxyURL is the URL of the proxy to use for the client
ProxyURL string `yaml:"proxy-url,omitempty"`
// Insecure determines whether to skip verifying the server's certificate chain and host name // Insecure determines whether to skip verifying the server's certificate chain and host name
Insecure bool `yaml:"insecure,omitempty"` Insecure bool `yaml:"insecure,omitempty"`
@ -184,6 +188,14 @@ func (c *Config) getHTTPClient() *http.Client {
return nil return nil
}, },
} }
if c.ProxyURL != "" {
proxyURL, err := url.Parse(c.ProxyURL)
if err != nil {
log.Println("[client][getHTTPClient] THIS SHOULD NOT HAPPEN. Silently ignoring custom proxy due to error:", err.Error())
} else {
c.httpClient.Transport.(*http.Transport).Proxy = http.ProxyURL(proxyURL)
}
}
if c.HasCustomDNSResolver() { if c.HasCustomDNSResolver() {
dnsResolver, err := c.parseDNSResolver() dnsResolver, err := c.parseDNSResolver()
if err != nil { if err != nil {

View File

@ -2,6 +2,7 @@ package client
import ( import (
"net/http" "net/http"
"net/url"
"testing" "testing"
"time" "time"
) )
@ -79,3 +80,29 @@ func TestConfig_ValidateAndSetDefaults_withCustomDNSResolver(t *testing.T) {
}) })
} }
} }
func TestConfig_getHTTPClient_withCustomProxyURL(t *testing.T) {
proxyURL := "http://proxy.example.com:8080"
cfg := &Config{
ProxyURL: proxyURL,
}
cfg.ValidateAndSetDefaults()
client := cfg.getHTTPClient()
transport := client.Transport.(*http.Transport)
if transport.Proxy == nil {
t.Errorf("expected Config.ProxyURL to set the HTTP client's proxy to %s", proxyURL)
}
req := &http.Request{
URL: &url.URL{
Scheme: "http",
Host: "www.example.com",
},
}
expectProxyURL, err := transport.Proxy(req)
if err != nil {
t.Errorf("can't proxy the request %s", proxyURL)
}
if proxyURL != expectProxyURL.String() {
t.Errorf("expected Config.ProxyURL to set the HTTP client's proxy to %s", proxyURL)
}
}