diff --git a/cmd/root.go b/cmd/root.go index 10aa74ca..8acc73bd 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -153,6 +153,7 @@ func rootCmdRun(cmd *cobra.Command, _ []string) { pclient := remote.New( config.Get().PanelLocation, remote.WithCredentials(t.ID, t.Token), + remote.WithCustomHeaders(config.Get().RemoteQuery.CustomHeaders), remote.WithHttpClient(&http.Client{ Timeout: time.Second * time.Duration(config.Get().RemoteQuery.Timeout), }), diff --git a/config/config.go b/config/config.go index a57d9c46..d1c08c72 100644 --- a/config/config.go +++ b/config/config.go @@ -126,6 +126,12 @@ type RemoteQueryConfiguration struct { // 50 servers is likely just as quick as two for 100 or one for 400, and will certainly // be less likely to cause performance issues on the Panel. BootServersPerPage int `default:"50" yaml:"boot_servers_per_page"` + + //When using services like Cloudflare Access to manage access to + //a specific system via an external authentication system, + //it is possible to add special headers to bypass authentication. + //The mentioned headers can be appended to queries sent from Wings to the panel. + CustomHeaders map[string]string `yaml:"custom_headers"` } // SystemConfiguration defines basic system configuration settings. diff --git a/remote/http.go b/remote/http.go index 006a7f44..855f697a 100644 --- a/remote/http.go +++ b/remote/http.go @@ -42,6 +42,7 @@ type client struct { tokenId string token string maxAttempts int + customHeaders map[string]string } // New returns a new HTTP request client that is used for making authenticated @@ -69,6 +70,13 @@ func WithCredentials(id, token string) ClientOption { } } +// WithCustomHeaders sets custom headers to be used when making remote requests. +func WithCustomHeaders(headers map[string]string) ClientOption { + return func(c *client) { + c.customHeaders = headers + } +} + // WithHttpClient sets the underlying HTTP client instance to use when making // requests to the Panel API. func WithHttpClient(httpClient *http.Client) ClientOption { @@ -110,6 +118,19 @@ func (c *client) requestOnce(ctx context.Context, method, path string, body io.R req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s.%s", c.tokenId, c.token)) + + // Apply custom headers, but prevent overriding critical headers + criticalHeaders := map[string]bool{ + "Authorization": true, + "User-Agent": true, + "Accept": true, + "Content-Type": true, + } + for key, value := range c.customHeaders { + if !criticalHeaders[key] { + req.Header.Set(key, value) + } + } // Call all opts functions to allow modifying the request for _, o := range opts {