Skip to content

Commit

Permalink
Add proxy related annotations (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
qiujian16 authored and GitHub Enterprise committed Jan 24, 2018
1 parent 0a0f3eb commit a17598d
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,23 @@
nginx based ingress controller to server all icp management service

The design doc is [here](https://github.ibm.com/IBMPrivateCloud/roadmap/blob/master/feature-specs/icp-router-refactor.md)

# annotations

| Name | Description | Values |
| --- | --- | --- |
| icp.management.ibm.com/auth-type | Authentication method for management service | string |
| icp.management.ibm.com/authz-type | Authorization method for management service | string |
| icp.management.ibm.com/rewrite-target | Target URI where the traffic must be redirected | string |
| icp.management.ibm.com/app-root | Base URI fort the server | string |
| icp.management.ibm.com/configuration-snippet | Additional configuration to the NGINX location | string |
| icp.management.ibm.com/secure-backends | uses https to reach the services | bool |
| icp.management.ibm.com/secure-verify-ca-secret | secret name that stores ca cert for upstream service | string |
| icp.management.ibm.com/secure-client-ca-secret | secret name that stores ca cert/key for client authentication of upstream server | string |
| icp.management.ibm.com/upstream-uri | URI of upstream | string |
| icp.management.ibm.com/location-modifier | Location modifier | string |
| icp.management.ibm.com/proxy-connect-timeout | proxy connect timeout | string |
| icp.management.ibm.com/proxy-send-timeout | proxy send timeout | string |
| icp.management.ibm.com/proxy-read-timeout | proxy read timeout | string |
| icp.management.ibm.com/proxy-buffer-size | buffer size of response | string |
| icp.management.ibm.com/proxy-body-size | max response body | string |
1 change: 1 addition & 0 deletions examples/metering-ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ metadata:
annotations:
kubernetes.io/ingress.class: "ibm-icp-management"
icp.management.ibm.com/auth-type: "id-token"
icp.management.ibm.com/rewrite-target: "/"
spec:
rules:
- http:
Expand Down
3 changes: 3 additions & 0 deletions pkg/ingress/annotations/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/authz"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/locationmodifier"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/parser"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/proxy"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/rewrite"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/secureupstream"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/snippet"
Expand All @@ -52,6 +53,7 @@ type Ingress struct {
Rewrite rewrite.Config
SecureUpstream secureupstream.Config
XForwardedPrefix bool
Proxy proxy.Config
}

// Extractor defines the annotation parsers to be used in the extraction of annotations
Expand All @@ -72,6 +74,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
"XForwardedPrefix": xforwardedprefix.NewParser(cfg),
"LocationModifier": locationmodifier.NewParser(cfg),
"UpstreamURI": upstreamuri.NewParser(cfg),
"Proxy": proxy.NewParser(cfg),
},
}
}
Expand Down
108 changes: 108 additions & 0 deletions pkg/ingress/annotations/proxy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
Copyright 2016 The Kubernetes 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 proxy

import (
extensions "k8s.io/api/extensions/v1beta1"

"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/parser"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/resolver"
)

var DefaultProxyConfig = Config{
BodySize: "1m",
ConnectTimeout: 5,
SendTimeout: 60,
ReadTimeout: 60,
BufferSize: "4k",
}

// Config returns the proxy timeout to use in the upstream server/s
type Config struct {
BodySize string `json:"bodySize"`
ConnectTimeout int `json:"connectTimeout"`
SendTimeout int `json:"sendTimeout"`
ReadTimeout int `json:"readTimeout"`
BufferSize string `json:"bufferSize"`
}

// Equal tests for equality between two Configuration types
func (l1 *Config) Equal(l2 *Config) bool {
if l1 == l2 {
return true
}
if l1 == nil || l2 == nil {
return false
}
if l1.BodySize != l2.BodySize {
return false
}
if l1.ConnectTimeout != l2.ConnectTimeout {
return false
}
if l1.SendTimeout != l2.SendTimeout {
return false
}
if l1.ReadTimeout != l2.ReadTimeout {
return false
}
if l1.BufferSize != l2.BufferSize {
return false
}

return true
}

type proxy struct {
r resolver.Resolver
}

// NewParser creates a new reverse proxy configuration annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return proxy{r}
}

// ParseAnnotations parses the annotations contained in the ingress
// rule used to configure upstream check parameters
func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
ct, err := parser.GetIntAnnotation("proxy-connect-timeout", ing)
if err != nil {
ct = DefaultProxyConfig.ConnectTimeout
}

st, err := parser.GetIntAnnotation("proxy-send-timeout", ing)
if err != nil {
st = DefaultProxyConfig.SendTimeout
}

rt, err := parser.GetIntAnnotation("proxy-read-timeout", ing)
if err != nil {
rt = DefaultProxyConfig.ReadTimeout
}

bufs, err := parser.GetStringAnnotation("proxy-buffer-size", ing)
if err != nil || bufs == "" {
bufs = DefaultProxyConfig.BufferSize
}

bs, err := parser.GetStringAnnotation("proxy-body-size", ing)
if err != nil || bs == "" {
bs = DefaultProxyConfig.BodySize
}

return &Config{bs, ct, st, rt, bufs}, nil
}
139 changes: 139 additions & 0 deletions pkg/ingress/annotations/proxy/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
Copyright 2016 The Kubernetes 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 proxy

import (
"testing"

api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/parser"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/resolver"
)

func buildIngress() *extensions.Ingress {
defaultBackend := extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
}

return &extensions.Ingress{
ObjectMeta: meta_v1.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: extensions.IngressSpec{
Backend: &extensions.IngressBackend{
ServiceName: "default-backend",
ServicePort: intstr.FromInt(80),
},
Rules: []extensions.IngressRule{
{
Host: "foo.bar.com",
IngressRuleValue: extensions.IngressRuleValue{
HTTP: &extensions.HTTPIngressRuleValue{
Paths: []extensions.HTTPIngressPath{
{
Path: "/foo",
Backend: defaultBackend,
},
},
},
},
},
},
},
}
}

type mockBackend struct {
resolver.Mock
}

func TestProxy(t *testing.T) {
ing := buildIngress()

data := map[string]string{}
data[parser.GetAnnotationWithPrefix("proxy-connect-timeout")] = "1"
data[parser.GetAnnotationWithPrefix("proxy-send-timeout")] = "2"
data[parser.GetAnnotationWithPrefix("proxy-read-timeout")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k"
ing.SetAnnotations(data)

i, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Fatalf("unexpected error parsing a valid")
}

p, ok := i.(*Config)
if !ok {
t.Fatalf("expected a Config type")
}
if !ok {
t.Fatalf("expected a Config type")
}
if p.ConnectTimeout != 1 {
t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout)
}
if p.SendTimeout != 2 {
t.Errorf("expected 2 as send-timeout but returned %v", p.SendTimeout)
}
if p.ReadTimeout != 3 {
t.Errorf("expected 3 as read-timeout but returned %v", p.ReadTimeout)
}
if p.BufferSize != "1k" {
t.Errorf("expected 1k as buffer-size but returned %v", p.BufferSize)
}
if p.BodySize != "2k" {
t.Errorf("expected 2k as body-size but returned %v", p.BodySize)
}
}

func TestProxyWithNoAnnotation(t *testing.T) {
ing := buildIngress()

data := map[string]string{}
ing.SetAnnotations(data)

i, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Fatalf("unexpected error parsing a valid")
}
p, ok := i.(*Config)
if !ok {
t.Fatalf("expected a Config type")
}
if p.ConnectTimeout != 5 {
t.Errorf("expected 5 as connect-timeout but returned %v", p.ConnectTimeout)
}
if p.SendTimeout != 60 {
t.Errorf("expected 60 as send-timeout but returned %v", p.SendTimeout)
}
if p.ReadTimeout != 60 {
t.Errorf("expected 60 as read-timeout but returned %v", p.ReadTimeout)
}
if p.BufferSize != "4k" {
t.Errorf("expected 4k as buffer-size but returned %v", p.BufferSize)
}
if p.BodySize != "1m" {
t.Errorf("expected 1m as body-size but returned %v", p.BodySize)
}
}
4 changes: 4 additions & 0 deletions pkg/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/class"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/parser"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/proxy"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/rewrite"
ngx_config "github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/controller/config"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/resolver"
Expand Down Expand Up @@ -296,6 +297,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
Rewrite: rewrite.Config{
Target: "/",
},
Proxy: proxy.DefaultProxyConfig,
},
}}

Expand Down Expand Up @@ -464,6 +466,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
loc.Ingress = ing
loc.ConfigurationSnippet = anns.ConfigurationSnippet
loc.Rewrite = anns.Rewrite
loc.Proxy = anns.Proxy
loc.XForwardedPrefix = anns.XForwardedPrefix
loc.AuthType = anns.AuthType
loc.AuthzType = anns.AuthzType
Expand All @@ -487,6 +490,7 @@ func (n *NGINXController) getBackendServers(ingresses []*extensions.Ingress) ([]
Ingress: ing,
ConfigurationSnippet: anns.ConfigurationSnippet,
Rewrite: anns.Rewrite,
Proxy: anns.Proxy,
XForwardedPrefix: anns.XForwardedPrefix,
AuthType: anns.AuthType,
AuthzType: anns.AuthzType,
Expand Down
5 changes: 5 additions & 0 deletions pkg/ingress/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/proxy"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/annotations/rewrite"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/resolver"
"github.ibm.com/IBMPrivateCloud/icp-management-ingress/pkg/ingress/store"
Expand Down Expand Up @@ -145,4 +146,8 @@ type Location struct {
LocationModifier string `json:"locationModifier,omitempty"`
// Upstream uri gives the additional uri to the current path of location
UpstreamURI string `json:"upstreamURI,omitempty"`
// Proxy contains information about timeouts and buffer sizes
// to be used in connections against endpoints
// +optional
Proxy proxy.Config `json:"proxy,omitempty"`
}
3 changes: 3 additions & 0 deletions pkg/ingress/types_equals.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ func (l1 *Location) Equal(l2 *Location) bool {
if l1.UpstreamURI != l2.UpstreamURI {
return false
}
if !(&l1.Proxy).Equal(&l2.Proxy) {
return false
}

return true
}
Expand Down
10 changes: 10 additions & 0 deletions rootfs/opt/ibm/router/nginx/template/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ http {
set $ingress_name "{{ $ing.Rule }}";
set $service_name "{{ $ing.Service }}";

client_max_body_size "{{ $location.Proxy.BodySize }}";

proxy_set_header Host $best_http_host;

# Allow websocket connections
Expand All @@ -237,6 +239,14 @@ http {
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";

proxy_connect_timeout {{ $location.Proxy.ConnectTimeout }}s;
proxy_send_timeout {{ $location.Proxy.SendTimeout }}s;
proxy_read_timeout {{ $location.Proxy.ReadTimeout }}s;

proxy_buffering off;
proxy_buffer_size "{{ $location.Proxy.BufferSize }}";
proxy_buffers 4 "{{ $location.Proxy.BufferSize }}";

{{/* Add any additional configuration defined */}}
{{ $location.ConfigurationSnippet }}

Expand Down

0 comments on commit a17598d

Please sign in to comment.