Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions config/v1/0000_10_config-operator_01_ingress.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,73 @@ spec:
domain:
description: "domain is used to generate a default host name for a route when the route's host name is empty. The generated host name will follow this pattern: \"<route-name>.<route-namespace>.<domain>\". \n It is also used as the default wildcard domain suffix for ingress. The default ingresscontroller domain will follow this pattern: \"*.<domain>\". \n Once set, changing domain is not currently supported."
type: string
requiredHSTSPolicies:
description: "requiredHSTSPolicies specifies HSTS policies that are required to be set on newly created or updated routes matching the domainPattern/s and namespaceSelector/s that are specified in the policy. Each requiredHSTSPolicy must have at least a domainPattern and a maxAge to validate a route HSTS Policy route annotation, and affect route admission. \n A candidate route is checked for HSTS Policies if it has the HSTS Policy route annotation: \"haproxy.router.openshift.io/hsts_header\" E.g. haproxy.router.openshift.io/hsts_header: max-age=31536000;preload;includeSubDomains \n - For each candidate route, if it matches a requiredHSTSPolicy domainPattern and optional namespaceSelector, then the maxAge, preloadPolicy, and includeSubdomainsPolicy must be valid to be admitted. Otherwise, the route is rejected. - The first match, by domainPattern and optional namespaceSelector, in the ordering of the RequiredHSTSPolicies determines the route's admission status. - If the candidate route doesn't match any requiredHSTSPolicy domainPattern and optional namespaceSelector, then it may use any HSTS Policy annotation. \n The HSTS policy configuration may be changed after routes have already been created. An update to a previously admitted route may then fail if the updated route does not conform to the updated HSTS policy configuration. However, changing the HSTS policy configuration will not cause a route that is already admitted to stop working. \n Note that if there are no RequiredHSTSPolicies, any HSTS Policy annotation on the route is valid."
type: array
items:
type: object
properties:
domainPatterns:
description: "domainPatterns is a list of domains for which the desired HSTS annotations are required. If domainPatterns is specified and a route is created with a spec.host matching one of the domains, the route must specify the HSTS Policy components described in the matching RequiredHSTSPolicy. \n The use of wildcards is allowed like this: *.foo.com matches everything under foo.com. foo.com only matches foo.com, so to cover foo.com and everything under it, you must specify *both*. kubebuilder:validation:MinLength=1"
type: array
items:
type: string
includeSubDomainsPolicy:
description: 'includeSubDomainsPolicy means the HSTS Policy should apply to any subdomains of the host''s domain name. Thus, for the host bar.foo.com, if includeSubDomainsPolicy was set to RequireIncludeSubDomains: - the host app.bar.foo.com would inherit the HSTS Policy of bar.foo.com - the host bar.foo.com would inherit the HSTS Policy of bar.foo.com - the host foo.com would NOT inherit the HSTS Policy of bar.foo.com - the host def.foo.com would NOT inherit the HSTS Policy of bar.foo.com'
type: string
enum:
- RequireIncludeSubDomains
- RequireNoIncludeSubDomains
- NoOpinion
maxAge:
description: maxAge is the delta time range in seconds during which hosts are regarded as HSTS hosts. If set to 0, it negates the effect, and hosts are removed as HSTS hosts. If set to 0 and includeSubdomains is specified, all subdomains of the host are also removed as HSTS hosts. maxAge is a time-to-live value, and if this policy is not refreshed on a client, the HSTS policy will eventually expire on that client.
type: object
properties:
largestMaxAge:
description: The largest allowed value (in seconds) of the RequiredHSTSPolicy max-age This value can be left unspecified, in which case no upper limit is enforced. kubebuilder:validation:minimum=0:maximum=2147483647
type: integer
format: int32
smallestMaxAge:
description: The smallest allowed value (in seconds) of the RequiredHSTSPolicy max-age Setting max-age=0 allows the deletion of an existing HSTS header from a host. This is a necessary tool for administrators to quickly correct mistakes. This value can be left unspecified, in which case no lower limit is enforced. kubebuilder:validation:minimum=0:maximum=2147483647
type: integer
format: int32
namespaceSelector:
description: namespaceSelector specifies a label selector such that the policy applies only to those routes that are in namespaces with labels that match the selector, and are in one of the DomainPatterns. Defaults to the empty LabelSelector, which matches everything.
type: object
properties:
matchExpressions:
description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
type: array
items:
description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
type: object
required:
- key
- operator
properties:
key:
description: key is the label key that the selector applies to.
type: string
operator:
description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
type: array
items:
type: string
matchLabels:
description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
type: object
additionalProperties:
type: string
preloadPolicy:
description: preloadPolicy directs the client to include hosts in its host preload list so that it never needs to do an initial load to get the HSTS header (note that this is not defined in RFC 6797 and is therefore client implementation-dependent).
type: string
enum:
- RequirePreload
- RequireNoPreload
- NoOpinion
status:
description: status holds observed values from the cluster. They may not be overridden.
type: object
Expand Down
86 changes: 86 additions & 0 deletions config/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,89 @@ type DelegatedAuthorization struct {
// disabled indicates that authorization should be disabled. By default it will use delegated authorization.
Disabled bool `json:"disabled,omitempty"`
}
type RequiredHSTSPolicy struct {
// namespaceSelector specifies a label selector such that the policy applies only to those routes that
// are in namespaces with labels that match the selector, and are in one of the DomainPatterns.
// Defaults to the empty LabelSelector, which matches everything.
// +optional
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`

// domainPatterns is a list of domains for which the desired HSTS annotations are required.
// If domainPatterns is specified and a route is created with a spec.host matching one of the domains,
// the route must specify the HSTS Policy components described in the matching RequiredHSTSPolicy.
//
// The use of wildcards is allowed like this: *.foo.com matches everything under foo.com.
// foo.com only matches foo.com, so to cover foo.com and everything under it, you must specify *both*.
// kubebuilder:validation:MinLength=1
// +required
DomainPatterns []string `json:"domainPatterns"`

// maxAge is the delta time range in seconds during which hosts are regarded as HSTS hosts.
// If set to 0, it negates the effect, and hosts are removed as HSTS hosts.
// If set to 0 and includeSubdomains is specified, all subdomains of the host are also removed as HSTS hosts.
// maxAge is a time-to-live value, and if this policy is not refreshed on a client, the HSTS
// policy will eventually expire on that client.
// +required
MaxAge MaxAgePolicy `json:"maxAge"`

// preloadPolicy directs the client to include hosts in its host preload list so that
// it never needs to do an initial load to get the HSTS header (note that this is not defined
// in RFC 6797 and is therefore client implementation-dependent).
// +optional
PreloadPolicy PreloadPolicy `json:"preloadPolicy,omitempty"`

// includeSubDomainsPolicy means the HSTS Policy should apply to any subdomains of the host's
// domain name. Thus, for the host bar.foo.com, if includeSubDomainsPolicy was set to RequireIncludeSubDomains:
// - the host app.bar.foo.com would inherit the HSTS Policy of bar.foo.com
// - the host bar.foo.com would inherit the HSTS Policy of bar.foo.com
// - the host foo.com would NOT inherit the HSTS Policy of bar.foo.com
// - the host def.foo.com would NOT inherit the HSTS Policy of bar.foo.com
// +optional
IncludeSubDomainsPolicy IncludeSubDomainsPolicy `json:"includeSubDomainsPolicy,omitempty"`
}

// MaxAgePolicy contains a numeric range for specifying a compliant HSTS max-age for the enclosing RequiredHSTSPolicy
type MaxAgePolicy struct {
// The largest allowed value (in seconds) of the RequiredHSTSPolicy max-age
// This value can be left unspecified, in which case no upper limit is enforced.
// kubebuilder:validation:minimum=0:maximum=2147483647
LargestMaxAge *int32 `json:"largestMaxAge,omitempty"`

// The smallest allowed value (in seconds) of the RequiredHSTSPolicy max-age
// Setting max-age=0 allows the deletion of an existing HSTS header from a host. This is a necessary
// tool for administrators to quickly correct mistakes.
// This value can be left unspecified, in which case no lower limit is enforced.
// kubebuilder:validation:minimum=0:maximum=2147483647
SmallestMaxAge *int32 `json:"smallestMaxAge,omitempty"`
}

// PreloadPolicy contains a value for specifying a compliant HSTS preload policy for the enclosing RequiredHSTSPolicy
// +kubebuilder:validation:Enum=RequirePreload;RequireNoPreload;NoOpinion
type PreloadPolicy string

const (
// RequirePreloadPolicy means HSTS "preload" is required by the RequiredHSTSPolicy
RequirePreloadPolicy PreloadPolicy = "RequirePreload"

// RequireNoPreloadPolicy means HSTS "preload" is forbidden by the RequiredHSTSPolicy
RequireNoPreloadPolicy PreloadPolicy = "RequireNoPreload"

// NoOpinionPreloadPolicy means HSTS "preload" doesn't matter to the RequiredHSTSPolicy
NoOpinionPreloadPolicy PreloadPolicy = "NoOpinion"
)

// IncludeSubDomainsPolicy contains a value for specifying a compliant HSTS includeSubdomains policy
// for the enclosing RequiredHSTSPolicy
// +kubebuilder:validation:Enum=RequireIncludeSubDomains;RequireNoIncludeSubDomains;NoOpinion
type IncludeSubDomainsPolicy string

const (
// RequireIncludeSubDomains means HSTS "includeSubDomains" is required by the RequiredHSTSPolicy
RequireIncludeSubDomains IncludeSubDomainsPolicy = "RequireIncludeSubDomains"

// RequireNoIncludeSubDomains means HSTS "includeSubDomains" is forbidden by the RequiredHSTSPolicy
RequireNoIncludeSubDomains IncludeSubDomainsPolicy = "RequireNoIncludeSubDomains"

// NoOpinionIncludeSubDomains means HSTS "includeSubDomains" doesn't matter to the RequiredHSTSPolicy
NoOpinionIncludeSubDomains IncludeSubDomainsPolicy = "NoOpinion"
)
25 changes: 25 additions & 0 deletions config/v1/types_ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,31 @@ type IngressSpec struct {
// configurable routes.
// +optional
ComponentRoutes []ComponentRouteSpec `json:"componentRoutes,omitempty"`

// requiredHSTSPolicies specifies HSTS policies that are required to be set on newly created or updated routes
// matching the domainPattern/s and namespaceSelector/s that are specified in the policy.
// Each requiredHSTSPolicy must have at least a domainPattern and a maxAge to validate a route HSTS Policy route
// annotation, and affect route admission.
//
// A candidate route is checked for HSTS Policies if it has the HSTS Policy route annotation:
// "haproxy.router.openshift.io/hsts_header"
// E.g. haproxy.router.openshift.io/hsts_header: max-age=31536000;preload;includeSubDomains
//
// - For each candidate route, if it matches a requiredHSTSPolicy domainPattern and optional namespaceSelector,
// then the maxAge, preloadPolicy, and includeSubdomainsPolicy must be valid to be admitted. Otherwise, the route
// is rejected.
// - The first match, by domainPattern and optional namespaceSelector, in the ordering of the RequiredHSTSPolicies
// determines the route's admission status.
// - If the candidate route doesn't match any requiredHSTSPolicy domainPattern and optional namespaceSelector,
// then it may use any HSTS Policy annotation.
//
// The HSTS policy configuration may be changed after routes have already been created. An update to a previously
// admitted route may then fail if the updated route does not conform to the updated HSTS policy configuration.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The admission plugin should only fail on a route update if hsts related values are changing. This statement is accurate if constrained to that case.

If you bump into some questions about how to make that ratchet, we can get into it on the admission PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.

// However, changing the HSTS policy configuration will not cause a route that is already admitted to stop working.
//
// Note that if there are no RequiredHSTSPolicies, any HSTS Policy annotation on the route is valid.
// +optional
RequiredHSTSPolicies []RequiredHSTSPolicy `json:"requiredHSTSPolicies,omitempty"`
}

// ConsumingUser is an alias for string which we add validation to. Currently only service accounts are supported.
Expand Down
60 changes: 60 additions & 0 deletions config/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading