Skip to content

Commit

Permalink
internal: add HTTPProxy authorization support
Browse files Browse the repository at this point in the history
Add HTTPProxy fields to specify how to bind a virtual host to
an ExtensionService that can authorize inbound client requests.

This fixes projectcontour#2715.

Signed-off-by: James Peach <[email protected]>
  • Loading branch information
jpeach committed Sep 11, 2020
1 parent e529626 commit 12b82e4
Show file tree
Hide file tree
Showing 17 changed files with 1,545 additions and 28 deletions.
80 changes: 80 additions & 0 deletions apis/projectcontour/v1/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright Project Contour Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1

// AuthorizationConfigured returns whether authorization is
// configured on this virtual host.
func (v *VirtualHost) AuthorizationConfigured() bool {
return v.TLS != nil && v.Authorization != nil
}

// DisableAuthorization returns true if this virtual host disables
// authorization. If an authorization server is present, the default
// policy is to not disable.
func (v *VirtualHost) DisableAuthorization() bool {
// No authorization, so it is disabled.
if v.AuthorizationConfigured() {
// No policy specified, default is to not disable.
if v.Authorization.AuthPolicy == nil {
return false
}

return v.Authorization.AuthPolicy.Disabled
}

return false
}

// AuthorizationContext returns the authorization policy context (if present).
func (v *VirtualHost) AuthorizationContext() map[string]string {
if v.AuthorizationConfigured() {
if v.Authorization.AuthPolicy != nil {
return v.Authorization.AuthPolicy.Context
}
}

return nil
}

// GetPrefixReplacements returns replacement prefixes from the path
// rewrite policy (if any).
func (r *Route) GetPrefixReplacements() []ReplacePrefix {
if r.PathRewritePolicy != nil {
return r.PathRewritePolicy.ReplacePrefix
}
return nil
}

// AuthorizationContext merges the parent context entries with the
// context from this Route. Common keys from the parent map will be
// overwritten by keys from the route. The parent map may be nil.
func (r *Route) AuthorizationContext(parent map[string]string) map[string]string {
values := make(map[string]string, len(parent))

for k, v := range parent {
values[k] = v
}

if r.AuthPolicy != nil {
for k, v := range r.AuthPolicy.Context {
values[k] = v
}
}

if len(values) == 0 {
return nil
}

return values
}
120 changes: 110 additions & 10 deletions apis/projectcontour/v1/httpproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,120 @@ type HeaderMatchCondition struct {
NotExact string `json:"notexact,omitempty"`
}

// ExtensionServiceReference names an ExtensionService resource.
type ExtensionServiceReference struct {
// API version of the referent.
// If this field is not specified, the default "projectcontour.io/v1alpha1" will be used
//
// +optional
// +kubebuilder:validation:MinLength=1
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,5,opt,name=apiVersion"`

// Kind of the referent.
// If this field is not specified, the default "ExtensionService" will be used.
//
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
//
// +optional
// +kubebuilder:validation:MinLength=1
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`

// Namespace of the referent.
// If this field is not specifies, the namespace of the resource that targets the referent will be used.
//
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
//
// +optional
// +kubebuilder:validation:MinLength=1
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"`

// Name of the referent.
//
// More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
//
// +required
// +kubebuilder:validation:MinLength=1
Name string `json:"name,omitempty" protobuf:"bytes,3,opt,name=name"`
}

// AuthorizationServer configures an external server to authenticate
// client requests. The external server must implement the Envoy
// external authorization GRPC protocol. Currently, the
// [v2](https://www.envoyproxy.io/docs/envoy/latest/api-v2/service/auth/v2/external_auth.proto)
// protocol is always used, but authorization server authors should implement
// the v3 protocol as well in the expectation that it will be supported
// in future.
type AuthorizationServer struct {
// ServiceRef specifies the extension resource that will authorize client requests.
//
// +required
ServiceRef ExtensionServiceReference `json:"serviceRef"`

// AuthPolicy sets a default authorization policy for client requests.
// This policy will be used unless overridden by individual routes.
//
// +optional
AuthPolicy *AuthorizationPolicy `json:"authPolicy,omitempty"`

// ResponseTimeout configures maximum time to wait for a check response from the authorization server.
// Timeout durations are expressed in the Go [Duration format](https://godoc.org/time#ParseDuration).
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
// The string "infinity" is also a valid input and specifies no timeout.
//
// +optional
ResponseTimeout string `json:"responseTimeout,omitempty"`

// If FailOpen is true, the client request is forwarded to the upstream service
// even if the authorization server fails to respond. This field should not be
// set in most cases. It is intended for use only while migrating applications
// from internal authorization to Contour external authorization.
//
// +optional
FailOpen bool `json:"failOpen,omitempty"`
}

// AuthorizationPolicy modifies how client requests are authenticated.
type AuthorizationPolicy struct {
// When true, this field disables client request authentication
// for the scope of the policy.
//
// +optional
Disabled bool `json:"disabled,omitempty"`

// Context is a set of key/value pairs that are sent to the
// authentication server in the check request. If a context
// is provided at an enclosing scope, the entries are merged
// such that the inner scope overrides matching keys from the
// outer scope.
//
// +optional
Context map[string]string `json:"context,omitempty"`
}

// VirtualHost appears at most once. If it is present, the object is considered
// to be a "root".
type VirtualHost struct {
// The fully qualified domain name of the root of the ingress tree
// all leaves of the DAG rooted at this object relate to the fqdn.
Fqdn string `json:"fqdn"`
// If present describes tls properties. The SNI names that will be matched on
// are described in fqdn, the tls.secretName secret must contain a
// certificate that itself contains a name that matches the FQDN.

// If present the fields describes TLS properties of the virtual
// host. The SNI names that will be matched on are described in fqdn,
// the tls.secretName secret must contain a certificate that itself
// contains a name that matches the FQDN.
//
// +optional
TLS *TLS `json:"tls,omitempty"`

// This field configures an extension service to perform
// authorization for this virtual host. Authorization can
// only be configured on virtual hosts that have TLS enabled.
// If the TLS configuration requires client certificate
///validation, the client certificate is always included in the
// authentication check request.
//
// +optional
Authorization *AuthorizationServer `json:"authorization,omitempty"`
}

// TLS describes tls properties. The SNI names that will be matched on
Expand Down Expand Up @@ -166,6 +269,10 @@ type Route struct {
// not permitted when a `virtualhost.tls` block is present.
// +optional
PermitInsecure bool `json:"permitInsecure,omitempty"`
// AuthPolicy updates the authorization policy for client
// requests that match this route.
// +optional
AuthPolicy *AuthorizationPolicy `json:"authPolicy,omitempty"`
// The timeout policy for this route.
// +optional
TimeoutPolicy *TimeoutPolicy `json:"timeoutPolicy,omitempty"`
Expand All @@ -191,13 +298,6 @@ type Route struct {
ResponseHeadersPolicy *HeadersPolicy `json:"responseHeadersPolicy,omitempty"`
}

func (r *Route) GetPrefixReplacements() []ReplacePrefix {
if r.PathRewritePolicy != nil {
return r.PathRewritePolicy.ReplacePrefix
}
return nil
}

// TCPProxy contains the set of services to proxy TCP connections.
type TCPProxy struct {
// The load balancing policy for the backend services.
Expand Down
68 changes: 68 additions & 0 deletions apis/projectcontour/v1/zz_generated.deepcopy.go

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

2 changes: 1 addition & 1 deletion cmd/contour/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func doServe(log logrus.FieldLogger, ctx *serveContext) error {

informerSyncList.InformOnResources(clusterInformerFactory, dynamicHandler, k8s.DefaultResources()...)

// Inform on ExtensionCluster resources if they are installed
// Inform on ExtensionService resources if they are installed
// in the cluster. TODO(jpeach) remove the resource check as part of #2711.
if gvr := projectcontourv1alpha1.GroupVersion.WithResource("extensionservices"); clients.ResourcesExist(gvr) {
informerSyncList.InformOnResources(clusterInformerFactory, dynamicHandler, gvr)
Expand Down
Loading

0 comments on commit 12b82e4

Please sign in to comment.