envoyutil

package
v1.20220411.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2022 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ErrMissingXFCC is the error returned when XFCC is missing.
	ErrMissingXFCC = ex.Class("Missing X-Forwarded-Client-Cert header")
	// ErrInvalidXFCC is the error returned when XFCC is invalid.
	ErrInvalidXFCC = ex.Class("Invalid X-Forwarded-Client-Cert header")
	// ErrInvalidClientIdentity is the error returned when XFCC has a
	// missing / invalid client identity.
	ErrInvalidClientIdentity = ex.Class("Client identity could not be determined from X-Forwarded-Client-Cert header")
	// ErrDeniedClientIdentity is the error returned when a parsed client identity is in a deny list or
	// not in an allow list.
	ErrDeniedClientIdentity = ex.Class("Client identity from X-Forwarded-Client-Cert header is denied")
	// ErrInvalidServerIdentity is the error returned when XFCC has a
	// missing / invalid client identity.
	ErrInvalidServerIdentity = ex.Class("Server identity could not be determined from X-Forwarded-Client-Cert header")
	// ErrDeniedServerIdentity is the error returned when a parsed client identity is in a deny list or
	// not in an allow list.
	ErrDeniedServerIdentity = ex.Class("Server identity from X-Forwarded-Client-Cert header is denied")
	// ErrMissingExtractFunction is the message used when the "extract client
	// identity" function is `nil` or not provided.
	ErrMissingExtractFunction = ex.Class("Missing client identity extraction function")
	// ErrVerifierNil is the message prefix used when a provided verifier is `nil`.
	ErrVerifierNil = ex.Class("XFCC verifier must not be `nil`")
)
View Source
const (
	// EnvVarWaitFlag is an environment variable which specifies whether
	// a wait function should wait for the Envoy Admin API to be ready.
	EnvVarWaitFlag = "WAIT_FOR_ENVOY"
	// EnvVarAdminPort is an environment variable which provides an override
	// for the Envoy Admin API port.
	EnvVarAdminPort = "ENVOY_ADMIN_PORT"
	// DefaultAdminPort is the default port used for the Envoy Admin API.
	DefaultAdminPort = "15000"
	// EnumStateLive is a `envoy.admin.v3.ServerInfo.State` value indicating
	// the Envoy server is LIVE. Other possible values of this enum are
	// DRAINING, PRE_INITIALIZING and INITIALIZING, but they are not used
	// here.
	// See: https://github.com/envoyproxy/envoy/blob/b867a4dfae32e600ea0a4087dc7925ded5e2ab2a/api/envoy/admin/v3/server_info.proto#L24-L36
	EnumStateLive = "LIVE"
)
View Source
const (
	// ErrXFCCParsing is the class of error returned when parsing XFCC fails
	ErrXFCCParsing = ex.Class("Error Parsing X-Forwarded-Client-Cert")
)
View Source
const (
	// HeaderXFCC is the header key for forwarded client cert
	HeaderXFCC = "x-forwarded-client-cert"
)

Variables

View Source
var (
	// ErrFailedAttempt is an error class returned when Envoy fails to be
	// ready on a single attempt.
	ErrFailedAttempt = ex.Class("Envoy not yet ready")
	// ErrTimedOut is an error class returned when Envoy fails to be ready
	// after exhausting all attempts.
	ErrTimedOut = ex.Class("Timed out waiting for Envoy to be ready")
)

Functions

func ClientIdentityAware

func ClientIdentityAware(cip IdentityProvider, verifiers ...VerifyXFCC) web.Middleware

ClientIdentityAware produces a middleware function nearly identical to `ClientIdentityRequired`. The primary difference is that this middleware will **not** return an error HTTP response for extraction or validation errors; it will still return a 500 Internal Server Error in unexpected failures. In cases of extraction or validation errors, the middleware will pass along to the next `action` and the client identity is not set on the current context.

func ClientIdentityRequired

func ClientIdentityRequired(cip IdentityProvider, verifiers ...VerifyXFCC) web.Middleware

ClientIdentityRequired produces a middleware function that determines the client identity used in a connection secured with mTLS.

This parses the `X-Forwarded-Client-Cert` (XFCC) from a request and uses a client identity provider (`cip`, e.g. see `SPIFFEClientIdentityProvider()`) to determine the client identity. Additionally, optional `verifiers` (e.g. see `SPIFFEServerIdentityProvider()`) can be used to verify other parts of the XFCC header such as the identity of the current server.

In cases of error, the client identity will not be set on the current context. For error status codes 400 and 401, the error will be serialized as JSON or XML (via `ctx.DefaultProvider`) and returned in the HTTP response. For error status code 500, no identifying information from the error will be returned in the HTTP response.

A 401 Unauthorized will be returned in the following cases:

  • The XFCC header is missing
  • The XFCC header (after parsing) contains zero elements or multiple elements (this code expects exactly one XFCC element, under the assumption that the Envoy `ForwardClientCertDetails` setting is configured to `SANITIZE_SET`)
  • The values from the XFCC header fail custom validation provided by `cip` or `verifiers`. For example, if the client identity is contained in a deny list, this would be considered a validation error.

A 400 Bad Request will be returned in the following cases:

  • The XFCC header cannot be parsed
  • Custom parsing / extraction done by `cip` fails. For example, in cases where the `URI` field in the XFCC is expected to be a valid SPIFFE URI with a valid Kubernetes workload identifier, if the `URI` field does not follow that format (e.g. `urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66`) this would be considered an extraction error.

A 500 Internal Server Error will be returned if the error is unrelated to validating the XFCC header or to parsing / extracting values from the XFCC header.

func ExtractAndVerifyClientIdentity

func ExtractAndVerifyClientIdentity(req *http.Request, cip IdentityProvider, verifiers ...VerifyXFCC) (string, error)

ExtractAndVerifyClientIdentity enables extracting client identity from a request. It does so by requiring the XFCC header to be present and valid and contain exactly one element. Then it passes the parsed XFCC header along to some `verifiers` (e.g. to verify the server identity) as well as to an extractor `cip` (for the client identity).

func GetClientIdentity

func GetClientIdentity(ctx context.Context) string

GetClientIdentity returns the client identity of the calling service or `""` if the client identity is unset.

For example if `rc` is a `*web.Ctx` ``` envoyutil.GetClientIdentity(rc.Context()) ```

func IsExtractionError

func IsExtractionError(err error) bool

IsExtractionError is a helper to check if an error is an `*XFCCExtractionError`.

func IsFatalError

func IsFatalError(err error) bool

IsFatalError is a helper to check if an error is an `*XFCCFatalError`.

func IsValidationError

func IsValidationError(err error) bool

IsValidationError is a helper to check if an error is an `*XFCCValidationError`.

func MaybeWaitForAdmin

func MaybeWaitForAdmin(log logger.Log) error

MaybeWaitForAdmin will check if Envoy is running if the `WAIT_FOR_ENVOY` environment variable is set. This will communicate with the Envoy admin port running on `localhost`, which defaults to 15000 but can be overridden with `ENVOY_ADMIN_PORT`. It will send `GET /ready` up to 10 times, sleeping for 1 second in between if the response is not 200 OK with a body of `LIVE\n`.

func WithClientIdentity

func WithClientIdentity(ctx context.Context, clientIdentity string) context.Context

WithClientIdentity adds the client identity to a context.

For example if `rc` is a `*web.Ctx` ``` rc.WithContext(envoyutil.WithClientIdentity(rc.Context(), clientIdentity)) ```

Types

type HTTPGetClient

type HTTPGetClient interface {
	Get(url string) (resp *http.Response, err error)
}

HTTPGetClient captures a small part of the `http.Client` interface needed to execute a GET request.

type IdentityFormatter

type IdentityFormatter = func(XFCCElement, *spiffeutil.ParsedURI) (string, error)

IdentityFormatter describes functions that will produce an identity string from a parsed SPIFFE URI.

type IdentityProcessor

type IdentityProcessor struct {
	Type                IdentityType
	AllowedTrustDomains []string
	DeniedTrustDomains  []string
	AllowedIdentities   collections.SetOfString
	DeniedIdentities    collections.SetOfString
	FormatIdentity      IdentityFormatter
}

IdentityProcessor provides configurable fields that can be used to help validate a parsed SPIFFE URI and produce and validate an identity from a parsed SPIFFE URI. The `Type` field determines if a client or server identity should be provided; by default the type will be client identity.

func (IdentityProcessor) IdentityProvider

func (ip IdentityProcessor) IdentityProvider(xfcc XFCCElement) (string, error)

IdentityProvider returns a client or server identity; it uses the configured rules to validate and format the identity by parsing the `URI` field (for client identity) or `By` field (for server identity) of the XFCC element. If `FormatIdentity` has not been specified, the `KubernetesIdentityFormatter()` method will be used as a fallback.

This method satisfies the `IdentityProvider` interface.

func (IdentityProcessor) KubernetesIdentityFormatter

func (ip IdentityProcessor) KubernetesIdentityFormatter(xfcc XFCCElement, pu *spiffeutil.ParsedURI) (string, error)

KubernetesIdentityFormatter assumes the SPIFFE URI contains a Kubernetes workload ID of the form `ns/{namespace}/sa/{serviceAccount}` and formats the identity as `{serviceAccount}.{namespace}`. This function satisfies the `IdentityFormatter` interface.

func (IdentityProcessor) ProcessAllowedIdentities

func (ip IdentityProcessor) ProcessAllowedIdentities(xfcc XFCCElement, identity string) error

ProcessAllowedIdentities returns an error if an allow list is configured and the identity does not match any elements in the list.

func (IdentityProcessor) ProcessAllowedTrustDomains

func (ip IdentityProcessor) ProcessAllowedTrustDomains(xfcc XFCCElement, pu *spiffeutil.ParsedURI) error

ProcessAllowedTrustDomains returns an error if an allow list is configured and the trust domain from the parsed SPIFFE URI does not match any elements in the list.

func (IdentityProcessor) ProcessDeniedIdentities

func (ip IdentityProcessor) ProcessDeniedIdentities(xfcc XFCCElement, identity string) error

ProcessDeniedIdentities returns an error if a denied list is configured and the identity matches any elements in the list.

func (IdentityProcessor) ProcessDeniedTrustDomains

func (ip IdentityProcessor) ProcessDeniedTrustDomains(xfcc XFCCElement, pu *spiffeutil.ParsedURI) error

ProcessDeniedTrustDomains returns an error if a denied list is configured and the trust domain from the parsed SPIFFE URI matches any elements in the list.

type IdentityProcessorOption

type IdentityProcessorOption func(*IdentityProcessor)

IdentityProcessorOption mutates an identity processor.

func OptAllowedIdentities

func OptAllowedIdentities(identities ...string) IdentityProcessorOption

OptAllowedIdentities adds allowed identities to the processor.

func OptAllowedTrustDomains

func OptAllowedTrustDomains(trustDomains ...string) IdentityProcessorOption

OptAllowedTrustDomains adds allowed trust domains to the processor.

func OptDeniedIdentities

func OptDeniedIdentities(identities ...string) IdentityProcessorOption

OptDeniedIdentities adds denied identities to the processor.

func OptDeniedTrustDomains

func OptDeniedTrustDomains(trustDomains ...string) IdentityProcessorOption

OptDeniedTrustDomains adds denied trust domains to the processor.

func OptFormatIdentity

func OptFormatIdentity(formatter IdentityFormatter) IdentityProcessorOption

OptFormatIdentity sets the `FormatIdentity` on the processor.

func OptIdentityType

func OptIdentityType(it IdentityType) IdentityProcessorOption

OptIdentityType sets the identity type for the processor.

type IdentityProvider

type IdentityProvider func(xfcc XFCCElement) (identity string, err error)

IdentityProvider is a function to extract the client or server identity from a parsed XFCC header. For example, client identity could be determined from the SPIFFE URI in the `URI` field in an XFCC element.

func SPIFFEClientIdentityProvider

func SPIFFEClientIdentityProvider(opts ...IdentityProcessorOption) IdentityProvider

SPIFFEClientIdentityProvider produces a function satisfying `IdentityProvider`.

This function assumes the client identity is in the `URI` field and that field is a SPIFFE URI.

It delegates processing of that SPIFFE URI via the `IdentityProcessor` type. The options supported can

  • Provide an allow list for the trust domain in the SPIFFE URI.
  • Provide a deny list for the trust domain in the SPIFFE URI.
  • Provide a function to produce a client identity string from the SPIFFE URI (likely from the workload ID in the SPIFFE URI); if no option is provided for this the default will use `IdentityProcessor.KubernetesIdentityFormatter`.
  • Provide an allow list for the client identity string.
  • Provide a deny list for the client identity string.

type IdentityType

type IdentityType int

IdentityType represents the type of identity that will be extracted by an `IdentityProcessor`. It can either be a client or server identity.

const (
	// ClientIdentity represents client identity.
	ClientIdentity IdentityType = 0
	// ServerIdentity represents server identity.
	ServerIdentity IdentityType = 1
)

type VerifyXFCC

type VerifyXFCC func(xfcc XFCCElement) error

VerifyXFCC is an "extra" verifier for an XFCC, for example if the server identity (from the `By` field in an XFCC element) should be verified in addition to the client identity.

func SPIFFEServerIdentityProvider

func SPIFFEServerIdentityProvider(opts ...IdentityProcessorOption) VerifyXFCC

SPIFFEServerIdentityProvider produces a verifier function satisfying `VerifyXFCC`.

This function assumes the server identity is in the `By` field and that field is a SPIFFE URI.

It delegates processing of that SPIFFE URI via the `IdentityProcessor` type. The options supported can

  • Provide an allow list for the trust domain in the SPIFFE URI.
  • Provide a deny list for the trust domain in the SPIFFE URI.
  • Provide a function to produce a server identity string from the SPIFFE URI (likely from the workload ID in the SPIFFE URI); if no option is provided for this the default will use `IdentityProcessor.KubernetesIdentityFormatter`.
  • Provide an allow list for the server identity string.
  • Provide a deny list for the server identity string.

type WaitForAdmin

type WaitForAdmin struct {
	// Port is the port (on localhost) where the Envoy Admin API is running.
	Port string
	// Sleep is the amount of time to sleep in between failed liveness
	// checks for the Envoy API.
	Sleep time.Duration
	// HTTPClient is the HTTP client to use when sending requests.
	HTTPClient HTTPGetClient
	// Log is an optional logger to be used when executing.
	Log logger.Log
	// Attempt is a counter for the number of attempts that have been made
	// to `executeOnce()`. This makes no attempt at "resetting" or guarding
	// against concurrent usage or re-usage of a `WaitForAdmin` struct.
	Attempt uint32
}

WaitForAdmin encapsulates the settings needed to wait until the Envoy Admin API is ready.

func (*WaitForAdmin) Execute

func (wfa *WaitForAdmin) Execute(ctx context.Context) error

Execute will communicate with the Envoy admin port running on `localhost`, which defaults to 15000 but can be overridden with `ENVOY_ADMIN_PORT`. It will send `GET /ready` up to 10 times, sleeping for `wfa.Sleep` in between if the response is not 200 OK with a body of `LIVE\n`.

func (*WaitForAdmin) IsReady

func (wfa *WaitForAdmin) IsReady() bool

IsReady makes a single request to the Envoy Admin API and checks if the status is ready.

type XFCC

type XFCC []XFCCElement

XFCC represents a proxy header containing certificate information for the client that is sending the request to the proxy. See https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-client-cert

func ParseXFCC

func ParseXFCC(header string) (XFCC, error)

ParseXFCC parses the XFCC header.

type XFCCElement

type XFCCElement struct {
	// By contains Subject Alternative Name (URI type) of the current proxy's
	// certificate.	This can be decoded as a `*url.URL` via `xe.DecodeBy()`.
	By string
	// Hash contains the SHA 256 digest of the current client certificate; this
	// is a string of 64 hexadecimal characters. This can be converted to the raw
	// bytes underlying the hex string via `xe.DecodeHash()`.
	Hash string
	// Cert contains the entire client certificate in URL encoded PEM format.
	// This can be decoded as a `*x509.Certificate` via `xe.DecodeCert()`.
	Cert string
	// Chain contains entire client certificate chain (including the leaf certificate)
	// in URL encoded PEM format. This can be decoded as a `[]*x509.Certificate` via
	// `xe.DecodeChain()`.
	Chain string
	// Subject contains the `Subject` field of the current client certificate.
	Subject string
	// URI contains the URI SAN of the current client certificate (assumes only
	// one URI SAN). This can be decoded as a `*url.URL` via `xe.DecodeURI()`.
	URI string
	// DNS contains the DNS SANs of the current client certificate. A client
	// certificate may contain multiple DNS SANs, each will be a separate
	// key-value pair in the XFCC element.
	DNS []string
}

XFCCElement is an element in an XFCC header (see `XFCC`).

func (XFCCElement) DecodeBy

func (xe XFCCElement) DecodeBy() (*url.URL, error)

DecodeBy decodes the `By` element from a URI string to a `*url.URL`.

func (XFCCElement) DecodeCert

func (xe XFCCElement) DecodeCert() (*x509.Certificate, error)

DecodeCert decodes the `Cert` element from a URL encoded PEM to a single `x509.Certificate`.

func (XFCCElement) DecodeChain

func (xe XFCCElement) DecodeChain() ([]*x509.Certificate, error)

DecodeChain decodes the `Chain` element from a URL encoded PEM to a `[]x509.Certificate`.

func (XFCCElement) DecodeHash

func (xe XFCCElement) DecodeHash() ([]byte, error)

DecodeHash decodes the `Hash` element from a hex string to raw bytes.

func (XFCCElement) DecodeURI

func (xe XFCCElement) DecodeURI() (*url.URL, error)

DecodeURI decodes the `URI` element from a URI string to a `*url.URL`.

func (XFCCElement) String

func (xe XFCCElement) String() string

String converts the parsed XFCC element **back** to a string. This is intended for debugging purposes and is not particularly

type XFCCExtractionError

type XFCCExtractionError struct {
	// Class can be used to uniquely identify the type of the error.
	Class ex.Class `json:"class" xml:"class"`
	// XFCC contains the XFCC header value that could not be parsed or was
	// invalid in some way.
	XFCC string `json:"xfcc,omitempty" xml:"xfcc,omitempty"`
	// Metadata contains extra information relevant to a specific failure.
	Metadata interface{} `json:"metadata,omitempty" xml:"metadata,omitempty"`
}

XFCCExtractionError contains metadata about an XFCC header that could not be parsed or extracted. This is intended to be used as the body of a 401 Unauthorized response.

func (*XFCCExtractionError) Error

func (xee *XFCCExtractionError) Error() string

Error satisfies the `error` interface. It is intended to be a unique identifier for the error.

type XFCCFatalError

type XFCCFatalError struct {
	// Class can be used to uniquely identify the type of the error.
	Class ex.Class `json:"class" xml:"class"`
	// XFCC contains the XFCC header value that could not be parsed or was
	// invalid in some way.
	XFCC string `json:"xfcc,omitempty" xml:"xfcc,omitempty"`
}

XFCCFatalError contains metadata about an unrecoverable failure when parsing an XFCC header. A "fatal error" should indicate invalid usage of `envoyutil` such as providing a `nil` value for a function interface that must be invoked.

func (*XFCCFatalError) Error

func (xfe *XFCCFatalError) Error() string

Error satisfies the `error` interface. It is intended to be a unique identifier for the error.

type XFCCValidationError

type XFCCValidationError struct {
	// Class can be used to uniquely identify the type of the error.
	Class ex.Class `json:"class" xml:"class"`
	// XFCC contains the XFCC header value that could not be parsed or was
	// invalid in some way.
	XFCC string `json:"xfcc,omitempty" xml:"xfcc,omitempty"`
	// Metadata contains extra information relevant to a specific failure.
	Metadata interface{} `json:"metadata,omitempty" xml:"metadata,omitempty"`
}

XFCCValidationError contains metadata about an XFCC header that could not be parsed or extracted. This is intended to be used as the body of a 401 Unauthorized response.

func (*XFCCValidationError) Error

func (xve *XFCCValidationError) Error() string

Error satisfies the `error` interface. It is intended to be a unique identifier for the error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL