mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 10:26:29 +02:00
147 lines
6.4 KiB
Go
147 lines
6.4 KiB
Go
package config // import "github.com/pomerium/pomerium/config"
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/pomerium/pomerium/internal/cryptutil"
|
|
"github.com/pomerium/pomerium/internal/urlutil"
|
|
)
|
|
|
|
// Policy contains route specific configuration and access settings.
|
|
type Policy struct {
|
|
From string `mapstructure:"from" yaml:"from"`
|
|
To string `mapstructure:"to" yaml:"to"`
|
|
// Identity related policy
|
|
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
|
|
AllowedGroups []string `mapstructure:"allowed_groups" yaml:"allowed_groups,omitempty" json:"allowed_groups,omitempty"`
|
|
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
|
|
|
|
Source *HostnameURL `yaml:",omitempty" json:"source,omitempty"`
|
|
Destination *url.URL `yaml:",omitempty" json:"destination,omitempty"`
|
|
|
|
// Allow unauthenticated HTTP OPTIONS requests as per the CORS spec
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
|
|
CORSAllowPreflight bool `mapstructure:"cors_allow_preflight" yaml:"cors_allow_preflight,omitempty"`
|
|
|
|
// Allow any public request to access this route. **Bypasses authentication**
|
|
AllowPublicUnauthenticatedAccess bool `mapstructure:"allow_public_unauthenticated_access" yaml:"allow_public_unauthenticated_access,omitempty"`
|
|
|
|
// UpstreamTimeout is the route specific timeout. Must be less than the global
|
|
// timeout. If unset, route will fallback to the proxy's DefaultUpstreamTimeout.
|
|
UpstreamTimeout time.Duration `mapstructure:"timeout" yaml:"timeout,omitempty"`
|
|
|
|
// Enable proxying of websocket connections by removing the default timeout handler.
|
|
// Caution: Enabling this feature could result in abuse via DOS attacks.
|
|
AllowWebsockets bool `mapstructure:"allow_websockets" yaml:"allow_websockets,omitempty"`
|
|
|
|
// TLSSkipVerify controls whether a client verifies the server's certificate
|
|
// chain and host name.
|
|
// If TLSSkipVerify is true, TLS accepts any certificate presented by the
|
|
// server and any host name in that certificate.
|
|
// In this mode, TLS is susceptible to man-in-the-middle attacks.
|
|
// This should be used only for testing.
|
|
TLSSkipVerify bool `mapstructure:"tls_skip_verify" yaml:"tls_skip_verify,omitempty"`
|
|
|
|
// TLSServerName overrides the hostname in the `to` field. This is useful
|
|
// if your backend is an HTTPS server with a valid certificate, but you
|
|
// want to communicate to the backend with an internal hostname (e.g.
|
|
// Docker container name).
|
|
TLSServerName string `mapstructure:"tls_server_name" yaml:"tls_server_name,omitempty"`
|
|
|
|
// TLSCustomCA defines the root certificate to use with a given
|
|
// route when verifying server certificates.
|
|
TLSCustomCA string `mapstructure:"tls_custom_ca" yaml:"tls_custom_ca,omitempty"`
|
|
TLSCustomCAFile string `mapstructure:"tls_custom_ca_file" yaml:"tls_custom_ca_file,omitempty"`
|
|
RootCAs *x509.CertPool `yaml:",omitempty"`
|
|
|
|
// Contains the x.509 client certificate to to present to the downstream
|
|
// host.
|
|
TLSClientCert string `mapstructure:"tls_client_cert" yaml:"tls_client_cert,omitempty"`
|
|
TLSClientKey string `mapstructure:"tls_client_key" yaml:"tls_client_key,omitempty"`
|
|
TLSClientCertFile string `mapstructure:"tls_client_cert_file" yaml:"tls_client_cert_file,omitempty"`
|
|
TLSClientKeyFile string `mapstructure:"tls_client_key_file" yaml:"tls_client_key_file,omitempty"`
|
|
ClientCertificate *tls.Certificate `yaml:",omitempty"`
|
|
|
|
// SetRequestHeaders adds a collection of headers to the downstream request
|
|
// in the form of key value pairs. Note bene, this will overwrite the
|
|
// value of any existing value of a given header key.
|
|
SetRequestHeaders map[string]string `mapstructure:"set_request_headers" yaml:"set_request_headers,omitempty"`
|
|
|
|
// PreserveHostHeader disables host header rewriting.
|
|
//
|
|
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
|
|
PreserveHostHeader bool `mapstructure:"preserve_host_header" yaml:"preserve_host_header,omitempty"`
|
|
}
|
|
|
|
// Validate checks the validity of a policy.
|
|
func (p *Policy) Validate() error {
|
|
var err error
|
|
source, err := urlutil.ParseAndValidateURL(p.From)
|
|
if err != nil {
|
|
return fmt.Errorf("config: policy bad source url %w", err)
|
|
}
|
|
p.Source = &HostnameURL{source}
|
|
|
|
p.Destination, err = urlutil.ParseAndValidateURL(p.To)
|
|
if err != nil {
|
|
return fmt.Errorf("config: policy bad destination url %w", err)
|
|
}
|
|
|
|
// Only allow public access if no other whitelists are in place
|
|
if p.AllowPublicUnauthenticatedAccess && (p.AllowedDomains != nil || p.AllowedGroups != nil || p.AllowedUsers != nil) {
|
|
return fmt.Errorf("config: policy route marked as public but contains whitelists")
|
|
}
|
|
|
|
if (p.TLSClientCert == "" && p.TLSClientKey != "") || (p.TLSClientCert != "" && p.TLSClientKey == "") ||
|
|
(p.TLSClientCertFile == "" && p.TLSClientKeyFile != "") || (p.TLSClientCertFile != "" && p.TLSClientKeyFile == "") {
|
|
return fmt.Errorf("config: client certificate key and cert both must be non-empty")
|
|
}
|
|
|
|
if p.TLSClientCert != "" && p.TLSClientKey != "" {
|
|
p.ClientCertificate, err = cryptutil.CertifcateFromBase64(p.TLSClientCert, p.TLSClientKey)
|
|
if err != nil {
|
|
return fmt.Errorf("config: couldn't decode client cert %w", err)
|
|
}
|
|
} else if p.TLSClientCertFile != "" && p.TLSClientKeyFile != "" {
|
|
p.ClientCertificate, err = cryptutil.CertificateFromFile(p.TLSClientCertFile, p.TLSClientKeyFile)
|
|
if err != nil {
|
|
return fmt.Errorf("config: couldn't load client cert file %w", err)
|
|
}
|
|
}
|
|
|
|
if p.TLSCustomCA != "" {
|
|
p.RootCAs, err = cryptutil.CertPoolFromBase64(p.TLSCustomCA)
|
|
if err != nil {
|
|
return fmt.Errorf("config: couldn't decode custom ca %w", err)
|
|
}
|
|
} else if p.TLSCustomCAFile != "" {
|
|
p.RootCAs, err = cryptutil.CertPoolFromFile(p.TLSCustomCAFile)
|
|
if err != nil {
|
|
return fmt.Errorf("config: couldn't load custom ca file %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Policy) String() string {
|
|
if p.Source == nil || p.Destination == nil {
|
|
return fmt.Sprintf("%s → %s", p.From, p.To)
|
|
}
|
|
return fmt.Sprintf("%s → %s", p.Source.String(), p.Destination.String())
|
|
}
|
|
|
|
// HostnameURL wraps url but marshals only the host representation of that
|
|
// url struct.
|
|
type HostnameURL struct {
|
|
*url.URL
|
|
}
|
|
|
|
// MarshalJSON returns the URLs host as json.
|
|
func (j *HostnameURL) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(j.Host)
|
|
}
|