2016-11-10 22:56:29 +00:00
/ *
Copyright 2015 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 controller
import (
"fmt"
2017-06-14 21:33:12 +00:00
"math/rand"
2017-09-24 20:36:59 +00:00
"net"
2016-11-10 22:56:29 +00:00
"reflect"
"sort"
"strconv"
"strings"
2017-09-19 20:09:33 +00:00
"sync/atomic"
2016-11-10 22:56:29 +00:00
"time"
"github.com/golang/glog"
2017-09-17 18:42:31 +00:00
apiv1 "k8s.io/api/core/v1"
2017-07-16 19:19:59 +00:00
extensions "k8s.io/api/extensions/v1beta1"
2017-08-25 15:50:08 +00:00
"k8s.io/apimachinery/pkg/conversion"
2017-04-01 14:39:42 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2017-08-15 12:32:26 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-04-01 14:39:42 +00:00
clientset "k8s.io/client-go/kubernetes"
2017-10-06 20:33:32 +00:00
2017-11-07 22:02:12 +00:00
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations"
"k8s.io/ingress-nginx/internal/ingress/annotations/class"
"k8s.io/ingress-nginx/internal/ingress/annotations/healthcheck"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/ingress/defaults"
"k8s.io/ingress-nginx/internal/ingress/resolver"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/task"
2016-11-10 22:56:29 +00:00
)
const (
2017-06-14 23:49:35 +00:00
defUpstreamName = "upstream-default-backend"
defServerName = "_"
rootLocation = "/"
2016-11-10 22:56:29 +00:00
)
var (
2017-11-05 01:18:28 +00:00
cloner * conversion . Cloner
2016-11-10 22:56:29 +00:00
)
2017-11-05 01:18:28 +00:00
func init ( ) {
cloner := conversion . NewCloner ( )
cloner . RegisterDeepCopyFunc ( ingress . GetGeneratedDeepCopyFuncs )
2016-11-10 22:56:29 +00:00
}
// Configuration contains all the settings required by an Ingress controller
type Configuration struct {
2017-11-08 20:58:57 +00:00
AnnotationsPrefix string
2017-11-05 01:18:28 +00:00
APIServerHost string
KubeConfigFile string
Client clientset . Interface
2016-11-10 22:56:29 +00:00
2017-11-05 01:18:28 +00:00
ResyncPeriod time . Duration
ConfigMapName string
2016-11-10 22:56:29 +00:00
DefaultService string
2017-11-22 13:40:54 +00:00
Namespace string
2017-04-13 01:50:54 +00:00
ForceNamespaceIsolation bool
2016-11-10 22:56:29 +00:00
// optional
TCPConfigMapName string
// optional
2017-11-05 01:18:28 +00:00
UDPConfigMapName string
2016-11-10 22:56:29 +00:00
DefaultHealthzURL string
2017-11-05 01:18:28 +00:00
DefaultSSLCertificate string
2016-11-10 22:56:29 +00:00
// optional
PublishService string
2017-01-20 22:01:37 +00:00
2017-06-20 01:22:08 +00:00
UpdateStatus bool
2017-10-08 17:29:19 +00:00
UseNodeInternalIP bool
2017-06-20 01:22:08 +00:00
ElectionID string
UpdateStatusOnShutdown bool
2017-10-08 17:29:19 +00:00
SortBackends bool
2017-02-07 18:13:08 +00:00
2017-11-05 01:18:28 +00:00
ListenPorts * ngx_config . ListenPorts
2017-08-25 15:50:08 +00:00
2017-11-05 01:18:28 +00:00
EnableSSLPassthrough bool
2016-11-10 22:56:29 +00:00
2017-11-05 01:18:28 +00:00
EnableProfiling bool
2016-11-10 22:56:29 +00:00
2017-11-13 01:43:28 +00:00
EnableSSLChainCompletion bool
2017-11-05 01:18:28 +00:00
FakeCertificatePath string
FakeCertificateSHA string
2016-11-10 22:56:29 +00:00
}
2016-12-29 20:02:06 +00:00
// GetDefaultBackend returns the default backend
2017-11-05 01:18:28 +00:00
func ( n NGINXController ) GetDefaultBackend ( ) defaults . Backend {
return n . backendDefaults
2016-12-29 20:02:06 +00:00
}
2017-09-17 16:34:29 +00:00
// GetPublishService returns the configured service used to set ingress status
2017-11-05 01:18:28 +00:00
func ( n NGINXController ) GetPublishService ( ) * apiv1 . Service {
s , err := n . listers . Service . GetByName ( n . cfg . PublishService )
2017-09-17 16:34:29 +00:00
if err != nil {
return nil
}
return s
}
2017-02-05 22:41:05 +00:00
// GetSecret searches for a secret in the local secrets Store
2017-11-05 01:18:28 +00:00
func ( n NGINXController ) GetSecret ( name string ) ( * apiv1 . Secret , error ) {
return n . listers . Secret . GetByName ( name )
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
// GetService searches for a service in the local secrets Store
2017-11-05 01:18:28 +00:00
func ( n NGINXController ) GetService ( name string ) ( * apiv1 . Service , error ) {
return n . listers . Service . GetByName ( name )
2016-11-10 22:56:29 +00:00
}
2017-11-08 20:58:57 +00:00
// GetAnnotationWithPrefix returns the prefix of ingress annotations
func ( n NGINXController ) GetAnnotationWithPrefix ( suffix string ) string {
return fmt . Sprintf ( "%v/%v" , n . cfg . AnnotationsPrefix , suffix )
}
2016-11-10 22:56:29 +00:00
// sync collects all the pieces required to assemble the configuration file and
// then sends the content to the backend (OnUpdate) receiving the populated
// template as response reloading the backend if is required.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) syncIngress ( item interface { } ) error {
n . syncRateLimiter . Accept ( )
2016-11-10 22:56:29 +00:00
2017-11-05 01:18:28 +00:00
if n . syncQueue . IsShuttingDown ( ) {
2016-11-10 22:56:29 +00:00
return nil
}
2017-09-28 00:33:23 +00:00
if element , ok := item . ( task . Element ) ; ok {
if name , ok := element . Key . ( string ) ; ok {
2017-11-05 01:18:28 +00:00
if obj , exists , _ := n . listers . Ingress . GetByKey ( name ) ; exists {
2017-09-28 00:33:23 +00:00
ing := obj . ( * extensions . Ingress )
2017-11-05 01:18:28 +00:00
n . readSecrets ( ing )
2017-09-28 00:33:23 +00:00
}
2017-07-27 17:46:33 +00:00
}
}
2017-09-25 19:40:55 +00:00
// Sort ingress rules using the ResourceVersion field
2017-11-05 01:18:28 +00:00
ings := n . listers . Ingress . List ( )
2017-09-25 19:40:55 +00:00
sort . SliceStable ( ings , func ( i , j int ) bool {
2017-09-26 18:05:20 +00:00
ir := ings [ i ] . ( * extensions . Ingress ) . ResourceVersion
jr := ings [ j ] . ( * extensions . Ingress ) . ResourceVersion
2017-09-25 19:40:55 +00:00
return ir < jr
} )
// filter ingress rules
var ingresses [ ] * extensions . Ingress
for _ , ingIf := range ings {
ing := ingIf . ( * extensions . Ingress )
2017-11-22 13:35:47 +00:00
if ! class . IsValid ( ing ) {
2017-09-25 19:40:55 +00:00
continue
}
ingresses = append ( ingresses , ing )
}
2017-11-05 01:18:28 +00:00
upstreams , servers := n . getBackendServers ( ingresses )
2016-11-11 23:43:35 +00:00
var passUpstreams [ ] * ingress . SSLPassthroughBackend
2017-06-12 12:45:08 +00:00
2016-11-10 22:56:29 +00:00
for _ , server := range servers {
if ! server . SSLPassthrough {
continue
}
for _ , loc := range server . Locations {
if loc . Path != rootLocation {
2017-04-02 02:32:22 +00:00
glog . Warningf ( "ignoring path %v of ssl passthrough host %v" , loc . Path , server . Hostname )
2016-11-10 22:56:29 +00:00
continue
}
2016-11-11 23:43:35 +00:00
passUpstreams = append ( passUpstreams , & ingress . SSLPassthroughBackend {
2016-11-16 18:24:26 +00:00
Backend : loc . Backend ,
Hostname : server . Hostname ,
2017-04-09 23:51:38 +00:00
Service : loc . Service ,
Port : loc . Port ,
2016-11-10 22:56:29 +00:00
} )
break
}
}
2017-09-13 19:15:47 +00:00
pcfg := ingress . Configuration {
2016-11-16 18:24:26 +00:00
Backends : upstreams ,
Servers : servers ,
2017-11-05 01:18:28 +00:00
TCPEndpoints : n . getStreamServices ( n . cfg . TCPConfigMapName , apiv1 . ProtocolTCP ) ,
UDPEndpoints : n . getStreamServices ( n . cfg . UDPConfigMapName , apiv1 . ProtocolUDP ) ,
2016-11-16 18:24:26 +00:00
PassthroughBackends : passUpstreams ,
2017-06-14 21:33:12 +00:00
}
2017-11-05 01:18:28 +00:00
if ! n . isForceReload ( ) && n . runningConfig != nil && n . runningConfig . Equal ( & pcfg ) {
2017-06-14 23:40:25 +00:00
glog . V ( 3 ) . Infof ( "skipping backend reload (no changes detected)" )
2017-06-14 21:33:12 +00:00
return nil
}
glog . Infof ( "backend reload required" )
2016-11-10 22:56:29 +00:00
2017-11-05 01:18:28 +00:00
err := n . OnUpdate ( pcfg )
2016-11-10 22:56:29 +00:00
if err != nil {
incReloadErrorCount ( )
2017-06-11 19:56:30 +00:00
glog . Errorf ( "unexpected failure restarting the backend: \n%v" , err )
2016-11-10 22:56:29 +00:00
return err
}
2017-06-11 19:56:30 +00:00
glog . Infof ( "ingress backend successfully reloaded..." )
incReloadCount ( )
2017-06-12 12:45:08 +00:00
setSSLExpireTime ( servers )
2017-06-11 19:56:30 +00:00
2017-11-05 01:18:28 +00:00
n . runningConfig = & pcfg
n . SetForceReload ( false )
2017-06-14 21:33:12 +00:00
2016-11-10 22:56:29 +00:00
return nil
}
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) getStreamServices ( configmapName string , proto apiv1 . Protocol ) [ ] ingress . L4Service {
2017-02-16 17:26:58 +00:00
glog . V ( 3 ) . Infof ( "obtaining information about stream services of type %v located in configmap %v" , proto , configmapName )
2017-02-02 22:41:02 +00:00
if configmapName == "" {
// no configmap configured
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-09-18 23:53:26 +00:00
_ , _ , err := k8s . ParseNameNS ( configmapName )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-09-18 23:53:26 +00:00
glog . Errorf ( "unexpected error reading configmap %v: %v" , configmapName , err )
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-11-05 01:18:28 +00:00
configmap , err := n . listers . ConfigMap . GetByName ( configmapName )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-09-18 23:53:26 +00:00
glog . Errorf ( "unexpected error reading configmap %v: %v" , configmapName , err )
2017-02-24 21:46:39 +00:00
return [ ] ingress . L4Service { }
2016-11-10 22:56:29 +00:00
}
2017-02-24 21:46:39 +00:00
var svcs [ ] ingress . L4Service
2017-09-27 14:10:16 +00:00
var svcProxyProtocol ingress . ProxyProtocol
2016-11-10 22:56:29 +00:00
// k -> port to expose
// v -> <namespace>/<service name>:<port from service to be used>
2017-02-02 22:41:02 +00:00
for k , v := range configmap . Data {
2017-02-24 21:46:39 +00:00
externalPort , err := strconv . Atoi ( k )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-02-02 22:41:02 +00:00
glog . Warningf ( "%v is not valid as a TCP/UDP port" , k )
2016-11-10 22:56:29 +00:00
continue
}
2017-11-05 21:55:31 +00:00
rp := [ ] int {
n . cfg . ListenPorts . HTTP ,
n . cfg . ListenPorts . HTTPS ,
n . cfg . ListenPorts . SSLProxy ,
n . cfg . ListenPorts . Status ,
n . cfg . ListenPorts . Health ,
n . cfg . ListenPorts . Default ,
}
if intInSlice ( externalPort , rp ) {
2016-11-10 22:56:29 +00:00
glog . Warningf ( "port %v cannot be used for TCP or UDP services. It is reserved for the Ingress controller" , k )
continue
}
nsSvcPort := strings . Split ( v , ":" )
2017-07-02 20:46:15 +00:00
if len ( nsSvcPort ) < 2 {
2017-09-27 14:10:16 +00:00
glog . Warningf ( "invalid format (namespace/name:port:[PROXY]:[PROXY]) '%v'" , k )
2016-11-10 22:56:29 +00:00
continue
}
nsName := nsSvcPort [ 0 ]
svcPort := nsSvcPort [ 1 ]
2017-09-27 14:10:16 +00:00
svcProxyProtocol . Decode = false
svcProxyProtocol . Encode = false
2017-07-02 20:46:15 +00:00
// Proxy protocol is possible if the service is TCP
2017-09-27 14:10:16 +00:00
if len ( nsSvcPort ) >= 3 && proto == apiv1 . ProtocolTCP {
if len ( nsSvcPort ) >= 3 && strings . ToUpper ( nsSvcPort [ 2 ] ) == "PROXY" {
svcProxyProtocol . Decode = true
}
if len ( nsSvcPort ) == 4 && strings . ToUpper ( nsSvcPort [ 3 ] ) == "PROXY" {
svcProxyProtocol . Encode = true
2017-07-02 20:46:15 +00:00
}
}
2016-11-10 22:56:29 +00:00
svcNs , svcName , err := k8s . ParseNameNS ( nsName )
if err != nil {
glog . Warningf ( "%v" , err )
continue
}
2017-11-05 01:18:28 +00:00
svcObj , svcExists , err := n . listers . Service . GetByKey ( nsName )
2016-11-10 22:56:29 +00:00
if err != nil {
glog . Warningf ( "error getting service %v: %v" , nsName , err )
continue
}
if ! svcExists {
glog . Warningf ( "service %v was not found" , nsName )
continue
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
2016-11-10 22:56:29 +00:00
2016-11-11 23:43:35 +00:00
var endps [ ] ingress . Endpoint
2016-11-10 22:56:29 +00:00
targetPort , err := strconv . Atoi ( svcPort )
if err != nil {
2017-09-18 23:53:26 +00:00
glog . V ( 3 ) . Infof ( "searching service %v endpoints using the name '%v'" , svcNs , svcName , svcPort )
2016-11-10 22:56:29 +00:00
for _ , sp := range svc . Spec . Ports {
if sp . Name == svcPort {
2017-08-07 03:22:06 +00:00
if sp . Protocol == proto {
2017-11-07 16:36:51 +00:00
endps = n . getEndpoints ( svc , & sp , proto , & healthcheck . Config { } )
2017-08-07 03:22:06 +00:00
break
}
2016-11-10 22:56:29 +00:00
}
}
} else {
// we need to use the TargetPort (where the endpoints are running)
2017-02-16 17:26:58 +00:00
glog . V ( 3 ) . Infof ( "searching service %v/%v endpoints using the target port '%v'" , svcNs , svcName , targetPort )
2016-11-10 22:56:29 +00:00
for _ , sp := range svc . Spec . Ports {
if sp . Port == int32 ( targetPort ) {
2017-08-07 03:22:06 +00:00
if sp . Protocol == proto {
2017-11-07 16:36:51 +00:00
endps = n . getEndpoints ( svc , & sp , proto , & healthcheck . Config { } )
2017-08-07 03:22:06 +00:00
break
}
2016-11-10 22:56:29 +00:00
}
}
}
2017-02-16 17:26:58 +00:00
// stream services cannot contain empty upstreams and there is no
// default backend equivalent
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
2017-02-16 17:26:58 +00:00
glog . Warningf ( "service %v/%v does not have any active endpoints for port %v and protocol %v" , svcNs , svcName , svcPort , proto )
2016-11-10 22:56:29 +00:00
continue
}
2017-02-24 21:46:39 +00:00
svcs = append ( svcs , ingress . L4Service {
Port : externalPort ,
Backend : ingress . L4Backend {
2017-09-27 14:10:16 +00:00
Name : svcName ,
Namespace : svcNs ,
Port : intstr . FromString ( svcPort ) ,
Protocol : proto ,
ProxyProtocol : svcProxyProtocol ,
2017-02-24 21:46:39 +00:00
} ,
Endpoints : endps ,
2016-11-10 22:56:29 +00:00
} )
}
return svcs
}
// getDefaultUpstream returns an upstream associated with the
// default backend service. In case of error retrieving information
// configure the upstream to return http code 503.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) getDefaultUpstream ( ) * ingress . Backend {
2016-11-11 23:43:35 +00:00
upstream := & ingress . Backend {
2016-11-10 22:56:29 +00:00
Name : defUpstreamName ,
}
2017-11-05 01:18:28 +00:00
svcKey := n . cfg . DefaultService
svcObj , svcExists , err := n . listers . Service . GetByKey ( svcKey )
2016-11-10 22:56:29 +00:00
if err != nil {
2017-11-05 01:18:28 +00:00
glog . Warningf ( "unexpected error searching the default backend %v: %v" , n . cfg . DefaultService , err )
upstream . Endpoints = append ( upstream . Endpoints , n . DefaultEndpoint ( ) )
2016-11-10 22:56:29 +00:00
return upstream
}
if ! svcExists {
2017-03-14 12:47:34 +00:00
glog . Warningf ( "service %v does not exist" , svcKey )
2017-11-05 01:18:28 +00:00
upstream . Endpoints = append ( upstream . Endpoints , n . DefaultEndpoint ( ) )
2016-11-10 22:56:29 +00:00
return upstream
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
2017-11-07 16:36:51 +00:00
endps := n . getEndpoints ( svc , & svc . Spec . Ports [ 0 ] , apiv1 . ProtocolTCP , & healthcheck . Config { } )
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
glog . Warningf ( "service %v does not have any active endpoints" , svcKey )
2017-11-05 01:18:28 +00:00
endps = [ ] ingress . Endpoint { n . DefaultEndpoint ( ) }
2016-11-10 22:56:29 +00:00
}
2017-08-11 02:51:32 +00:00
upstream . Service = svc
2016-11-11 23:43:35 +00:00
upstream . Endpoints = append ( upstream . Endpoints , endps ... )
2016-11-10 22:56:29 +00:00
return upstream
}
2016-11-11 23:43:35 +00:00
// getBackendServers returns a list of Upstream and Server to be used by the backend
2016-11-10 22:56:29 +00:00
// An upstream can be used in multiple servers if the namespace, service name and port are the same
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) getBackendServers ( ingresses [ ] * extensions . Ingress ) ( [ ] * ingress . Backend , [ ] * ingress . Server ) {
du := n . getDefaultUpstream ( )
upstreams := n . createUpstreams ( ingresses , du )
servers := n . createServers ( ingresses , upstreams , du )
2016-11-10 22:56:29 +00:00
2017-09-25 19:40:55 +00:00
for _ , ing := range ingresses {
2017-11-07 16:36:51 +00:00
anns := n . getIngressAnnotations ( ing )
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
server := servers [ host ]
if server == nil {
server = servers [ defServerName ]
}
if rule . HTTP == nil &&
host != defServerName {
2017-04-03 10:22:08 +00:00
glog . V ( 3 ) . Infof ( "ingress rule %v/%v does not contain HTTP rules, using default backend" , ing . Namespace , ing . Name )
2016-11-10 22:56:29 +00:00
continue
}
2017-08-22 20:16:59 +00:00
if server . CertificateAuth . CAFileName == "" {
2017-11-07 16:36:51 +00:00
server . CertificateAuth = anns . CertificateAuth
// It is possible that no CAFileName is found in the secret
if server . CertificateAuth . CAFileName == "" {
glog . V ( 3 ) . Infof ( "secret %v does not contain 'ca.crt', mutual authentication not enabled - ingress rule %v/%v." , server . CertificateAuth . Secret , ing . Namespace , ing . Name )
2017-08-22 20:16:59 +00:00
}
} else {
2017-09-27 03:46:22 +00:00
glog . V ( 3 ) . Infof ( "server %v already contains a mutual authentication configuration - ingress rule %v/%v" , server . Hostname , ing . Namespace , ing . Name )
2017-08-22 20:16:59 +00:00
}
2016-11-10 22:56:29 +00:00
for _ , path := range rule . HTTP . Paths {
upsName := fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
path . Backend . ServiceName ,
path . Backend . ServicePort . String ( ) )
ups := upstreams [ upsName ]
// if there's no path defined we assume /
nginxPath := rootLocation
if path . Path != "" {
nginxPath = path . Path
}
addLoc := true
for _ , loc := range server . Locations {
if loc . Path == nginxPath {
addLoc = false
if ! loc . IsDefBackend {
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "avoiding replacement of ingress rule %v/%v location %v upstream %v (%v)" , ing . Namespace , ing . Name , loc . Path , ups . Name , loc . Backend )
2016-11-10 22:56:29 +00:00
break
}
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "replacing ingress rule %v/%v location %v upstream %v (%v)" , ing . Namespace , ing . Name , loc . Path , ups . Name , loc . Backend )
loc . Backend = ups . Name
2016-11-10 22:56:29 +00:00
loc . IsDefBackend = false
2016-11-16 18:24:26 +00:00
loc . Backend = ups . Name
2017-04-09 23:51:38 +00:00
loc . Port = ups . Port
loc . Service = ups . Service
2017-08-25 15:50:08 +00:00
loc . Ingress = ing
2017-11-07 16:36:51 +00:00
loc . BasicDigestAuth = anns . BasicDigestAuth
loc . ClientBodyBufferSize = anns . ClientBodyBufferSize
loc . ConfigurationSnippet = anns . ConfigurationSnippet
loc . CorsConfig = anns . CorsConfig
loc . ExternalAuth = anns . ExternalAuth
loc . Proxy = anns . Proxy
loc . RateLimit = anns . RateLimit
loc . Redirect = anns . Redirect
loc . Rewrite = anns . Rewrite
loc . UpstreamVhost = anns . UpstreamVhost
loc . VtsFilterKey = anns . VtsFilterKey
loc . Whitelist = anns . Whitelist
2017-11-08 21:36:03 +00:00
loc . Denied = anns . Denied
2017-11-07 16:36:51 +00:00
2017-08-19 21:13:02 +00:00
if loc . Redirect . FromToWWW {
server . RedirectFromToWWW = true
}
2016-11-10 22:56:29 +00:00
break
}
}
// is a new location
if addLoc {
2016-11-16 18:24:26 +00:00
glog . V ( 3 ) . Infof ( "adding location %v in ingress rule %v/%v upstream %v" , nginxPath , ing . Namespace , ing . Name , ups . Name )
2016-12-29 20:02:06 +00:00
loc := & ingress . Location {
2017-11-07 16:36:51 +00:00
Path : nginxPath ,
Backend : ups . Name ,
IsDefBackend : false ,
Service : ups . Service ,
Port : ups . Port ,
Ingress : ing ,
BasicDigestAuth : anns . BasicDigestAuth ,
ClientBodyBufferSize : anns . ClientBodyBufferSize ,
ConfigurationSnippet : anns . ConfigurationSnippet ,
CorsConfig : anns . CorsConfig ,
ExternalAuth : anns . ExternalAuth ,
Proxy : anns . Proxy ,
RateLimit : anns . RateLimit ,
Redirect : anns . Redirect ,
Rewrite : anns . Rewrite ,
UpstreamVhost : anns . UpstreamVhost ,
VtsFilterKey : anns . VtsFilterKey ,
Whitelist : anns . Whitelist ,
2017-11-08 21:36:03 +00:00
Denied : anns . Denied ,
2016-12-29 20:02:06 +00:00
}
2017-11-07 16:36:51 +00:00
2017-08-19 21:13:02 +00:00
if loc . Redirect . FromToWWW {
server . RedirectFromToWWW = true
}
2016-12-29 20:02:06 +00:00
server . Locations = append ( server . Locations , loc )
2016-11-10 22:56:29 +00:00
}
2017-06-26 01:30:30 +00:00
if ups . SessionAffinity . AffinityType == "" {
2017-11-07 16:36:51 +00:00
ups . SessionAffinity . AffinityType = anns . SessionAffinity . Type
2017-06-26 01:30:30 +00:00
}
2017-11-07 16:36:51 +00:00
if anns . SessionAffinity . Type == "cookie" {
ups . SessionAffinity . CookieSessionAffinity . Name = anns . SessionAffinity . Cookie . Name
ups . SessionAffinity . CookieSessionAffinity . Hash = anns . SessionAffinity . Cookie . Hash
2017-06-26 01:30:30 +00:00
locs := ups . SessionAffinity . CookieSessionAffinity . Locations
if _ , ok := locs [ host ] ; ! ok {
locs [ host ] = [ ] string { }
}
locs [ host ] = append ( locs [ host ] , path . Path )
}
2016-11-10 22:56:29 +00:00
}
}
}
2017-08-25 15:50:08 +00:00
aUpstreams := make ( [ ] * ingress . Backend , 0 , len ( upstreams ) )
2017-04-02 02:32:22 +00:00
for _ , upstream := range upstreams {
isHTTPSfrom := [ ] * ingress . Server { }
for _ , server := range servers {
for _ , location := range server . Locations {
if upstream . Name == location . Backend {
2017-08-25 15:50:08 +00:00
if len ( upstream . Endpoints ) == 0 {
2017-10-09 12:16:09 +00:00
glog . V ( 3 ) . Infof ( "upstream %v does not have any active endpoints." , upstream . Name )
location . Backend = ""
2017-08-25 15:50:08 +00:00
// check if the location contains endpoints and a custom default backend
if location . DefaultBackend != nil {
sp := location . DefaultBackend . Spec . Ports [ 0 ]
2017-11-07 16:36:51 +00:00
endps := n . getEndpoints ( location . DefaultBackend , & sp , apiv1 . ProtocolTCP , & healthcheck . Config { } )
2017-08-25 15:50:08 +00:00
if len ( endps ) > 0 {
glog . V ( 3 ) . Infof ( "using custom default backend in server %v location %v (service %v/%v)" ,
server . Hostname , location . Path , location . DefaultBackend . Namespace , location . DefaultBackend . Name )
b , err := cloner . DeepCopy ( upstream )
2017-08-25 23:49:44 +00:00
if err != nil {
glog . Errorf ( "unexpected error copying Upstream: %v" , err )
} else {
2017-08-25 15:50:08 +00:00
name := fmt . Sprintf ( "custom-default-backend-%v" , upstream . Name )
nb := b . ( * ingress . Backend )
nb . Name = name
nb . Endpoints = endps
aUpstreams = append ( aUpstreams , nb )
location . Backend = name
}
}
}
}
// Configure Backends[].SSLPassthrough
2017-04-02 02:32:22 +00:00
if server . SSLPassthrough {
if location . Path == rootLocation {
if location . Backend == defUpstreamName {
glog . Warningf ( "ignoring ssl passthrough of %v as it doesn't have a default backend (root context)" , server . Hostname )
2017-04-09 23:51:38 +00:00
continue
2017-04-02 02:32:22 +00:00
}
2017-04-09 23:51:38 +00:00
isHTTPSfrom = append ( isHTTPSfrom , server )
2017-04-02 02:32:22 +00:00
}
}
}
}
}
2017-08-25 15:50:08 +00:00
2017-04-02 02:32:22 +00:00
if len ( isHTTPSfrom ) > 0 {
2017-04-09 23:51:38 +00:00
upstream . SSLPassthrough = true
2017-04-02 02:32:22 +00:00
}
}
2017-08-25 15:50:08 +00:00
// create the list of upstreams and skip those without endpoints
for _ , upstream := range upstreams {
if len ( upstream . Endpoints ) == 0 {
continue
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
aUpstreams = append ( aUpstreams , upstream )
2016-11-10 22:56:29 +00:00
}
2017-08-25 15:50:08 +00:00
2017-11-05 01:18:28 +00:00
if n . cfg . SortBackends {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( aUpstreams , func ( a , b int ) bool {
2017-09-18 23:53:26 +00:00
return aUpstreams [ a ] . Name < aUpstreams [ b ] . Name
} )
2017-07-13 00:35:36 +00:00
}
2016-11-10 22:56:29 +00:00
aServers := make ( [ ] * ingress . Server , 0 , len ( servers ) )
for _ , value := range servers {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( value . Locations , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
return value . Locations [ i ] . Path > value . Locations [ j ] . Path
} )
2016-11-10 22:56:29 +00:00
aServers = append ( aServers , value )
}
2017-09-18 23:53:26 +00:00
2017-09-25 19:40:55 +00:00
sort . SliceStable ( aServers , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
return aServers [ i ] . Hostname < aServers [ j ] . Hostname
} )
2016-11-10 22:56:29 +00:00
return aUpstreams , aServers
}
2017-09-27 03:46:22 +00:00
// GetAuthCertificate is used by the auth-tls annotations to get a cert from a secret
2017-11-05 01:18:28 +00:00
func ( n NGINXController ) GetAuthCertificate ( name string ) ( * resolver . AuthSSLCert , error ) {
if _ , exists := n . sslCertTracker . Get ( name ) ; ! exists {
n . syncSecret ( name )
2017-05-23 17:20:31 +00:00
}
2017-11-05 01:18:28 +00:00
_ , err := n . listers . Secret . GetByName ( name )
2017-02-06 18:16:36 +00:00
if err != nil {
return & resolver . AuthSSLCert { } , fmt . Errorf ( "unexpected error: %v" , err )
}
2017-11-05 01:18:28 +00:00
bc , exists := n . sslCertTracker . Get ( name )
2016-11-10 22:56:29 +00:00
if ! exists {
2017-10-01 00:48:14 +00:00
return & resolver . AuthSSLCert { } , fmt . Errorf ( "secret %v does not exist" , name )
2016-11-10 22:56:29 +00:00
}
cert := bc . ( * ingress . SSLCert )
2017-01-10 12:16:18 +00:00
return & resolver . AuthSSLCert {
2017-10-01 00:48:14 +00:00
Secret : name ,
2017-02-06 18:16:36 +00:00
CAFileName : cert . CAFileName ,
PemSHA : cert . PemSHA ,
2016-11-10 22:56:29 +00:00
} , nil
}
// createUpstreams creates the NGINX upstreams for each service referenced in
// Ingress rules. The servers inside the upstream are endpoints.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) createUpstreams ( data [ ] * extensions . Ingress , du * ingress . Backend ) map [ string ] * ingress . Backend {
2016-11-11 23:43:35 +00:00
upstreams := make ( map [ string ] * ingress . Backend )
2017-09-18 23:53:26 +00:00
upstreams [ defUpstreamName ] = du
2016-11-10 22:56:29 +00:00
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2017-11-07 16:36:51 +00:00
anns := n . getIngressAnnotations ( ing )
2016-11-10 22:56:29 +00:00
var defBackend string
if ing . Spec . Backend != nil {
defBackend = fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
ing . Spec . Backend . ServiceName ,
ing . Spec . Backend . ServicePort . String ( ) )
glog . V ( 3 ) . Infof ( "creating upstream %v" , defBackend )
upstreams [ defBackend ] = newUpstream ( defBackend )
svcKey := fmt . Sprintf ( "%v/%v" , ing . GetNamespace ( ) , ing . Spec . Backend . ServiceName )
2017-07-16 20:19:45 +00:00
2017-09-13 19:15:47 +00:00
// Add the service cluster endpoint as the upstream instead of individual endpoints
// if the serviceUpstream annotation is enabled
2017-11-07 16:36:51 +00:00
if anns . ServiceUpstream {
2017-11-05 01:18:28 +00:00
endpoint , err := n . getServiceClusterEndpoint ( svcKey , ing . Spec . Backend )
2017-09-13 19:15:47 +00:00
if err != nil {
glog . Errorf ( "Failed to get service cluster endpoint for service %s: %v" , svcKey , err )
} else {
upstreams [ defBackend ] . Endpoints = [ ] ingress . Endpoint { endpoint }
}
}
if len ( upstreams [ defBackend ] . Endpoints ) == 0 {
2017-11-07 16:36:51 +00:00
endps , err := n . serviceEndpoints ( svcKey , ing . Spec . Backend . ServicePort . String ( ) , & anns . HealthCheck )
2017-09-13 19:15:47 +00:00
upstreams [ defBackend ] . Endpoints = append ( upstreams [ defBackend ] . Endpoints , endps ... )
if err != nil {
glog . Warningf ( "error creating upstream %v: %v" , defBackend , err )
}
}
2016-11-10 22:56:29 +00:00
}
for _ , rule := range ing . Spec . Rules {
if rule . HTTP == nil {
continue
}
for _ , path := range rule . HTTP . Paths {
name := fmt . Sprintf ( "%v-%v-%v" ,
ing . GetNamespace ( ) ,
path . Backend . ServiceName ,
path . Backend . ServicePort . String ( ) )
2017-04-09 23:51:38 +00:00
if _ , ok := upstreams [ name ] ; ok {
continue
2016-11-10 22:56:29 +00:00
}
2017-04-09 23:51:38 +00:00
glog . V ( 3 ) . Infof ( "creating upstream %v" , name )
upstreams [ name ] = newUpstream ( name )
2017-08-11 02:51:32 +00:00
upstreams [ name ] . Port = path . Backend . ServicePort
2017-04-09 23:51:38 +00:00
if ! upstreams [ name ] . Secure {
2017-11-07 16:36:51 +00:00
upstreams [ name ] . Secure = anns . SecureUpstream . Secure
2017-05-14 22:14:27 +00:00
}
2017-06-16 00:43:17 +00:00
2017-05-14 22:14:27 +00:00
if upstreams [ name ] . SecureCACert . Secret == "" {
2017-11-07 16:36:51 +00:00
upstreams [ name ] . SecureCACert = anns . SecureUpstream . CACert
2016-11-16 18:24:26 +00:00
}
2017-06-16 00:43:17 +00:00
2017-09-30 21:29:16 +00:00
if upstreams [ name ] . UpstreamHashBy == "" {
2017-11-07 16:36:51 +00:00
upstreams [ name ] . UpstreamHashBy = anns . UpstreamHashBy
2017-09-30 21:29:16 +00:00
}
2017-04-09 23:51:38 +00:00
svcKey := fmt . Sprintf ( "%v/%v" , ing . GetNamespace ( ) , path . Backend . ServiceName )
2017-07-16 20:19:45 +00:00
2017-09-13 19:15:47 +00:00
// Add the service cluster endpoint as the upstream instead of individual endpoints
// if the serviceUpstream annotation is enabled
2017-11-07 16:36:51 +00:00
if anns . ServiceUpstream {
2017-11-05 01:18:28 +00:00
endpoint , err := n . getServiceClusterEndpoint ( svcKey , & path . Backend )
2017-09-13 19:15:47 +00:00
if err != nil {
glog . Errorf ( "failed to get service cluster endpoint for service %s: %v" , svcKey , err )
} else {
upstreams [ name ] . Endpoints = [ ] ingress . Endpoint { endpoint }
}
}
if len ( upstreams [ name ] . Endpoints ) == 0 {
2017-11-07 16:36:51 +00:00
endp , err := n . serviceEndpoints ( svcKey , path . Backend . ServicePort . String ( ) , & anns . HealthCheck )
2017-09-13 19:15:47 +00:00
if err != nil {
glog . Warningf ( "error obtaining service endpoints: %v" , err )
continue
}
upstreams [ name ] . Endpoints = endp
}
2017-04-09 23:51:38 +00:00
2017-11-05 01:18:28 +00:00
s , err := n . listers . Service . GetByName ( svcKey )
2017-04-09 23:51:38 +00:00
if err != nil {
glog . Warningf ( "error obtaining service: %v" , err )
continue
}
2017-09-18 23:53:26 +00:00
upstreams [ name ] . Service = s
2016-11-10 22:56:29 +00:00
}
}
}
return upstreams
}
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) getServiceClusterEndpoint ( svcKey string , backend * extensions . IngressBackend ) ( endpoint ingress . Endpoint , err error ) {
svcObj , svcExists , err := n . listers . Service . GetByKey ( svcKey )
2017-07-16 20:19:45 +00:00
if ! svcExists {
return endpoint , fmt . Errorf ( "service %v does not exist" , svcKey )
}
2017-09-17 18:42:31 +00:00
svc := svcObj . ( * apiv1 . Service )
2017-10-26 23:23:48 +00:00
if svc . Spec . ClusterIP == "" || svc . Spec . ClusterIP == "None" {
2017-07-16 20:19:45 +00:00
return endpoint , fmt . Errorf ( "No ClusterIP found for service %s" , svcKey )
}
endpoint . Address = svc . Spec . ClusterIP
2017-10-26 22:48:50 +00:00
// If the service port in the ingress uses a name, lookup
// the actual port in the service spec
if backend . ServicePort . Type == intstr . String {
var port int32 = - 1
for _ , svcPort := range svc . Spec . Ports {
if svcPort . Name == backend . ServicePort . String ( ) {
port = svcPort . Port
break
}
}
if port == - 1 {
return endpoint , fmt . Errorf ( "no port mapped for service %s and port name %s" , svc . Name , backend . ServicePort . String ( ) )
}
endpoint . Port = fmt . Sprintf ( "%d" , port )
} else {
endpoint . Port = backend . ServicePort . String ( )
}
2017-07-16 20:19:45 +00:00
return endpoint , err
}
2016-11-10 22:56:29 +00:00
// serviceEndpoints returns the upstream servers (endpoints) associated
// to a service.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) serviceEndpoints ( svcKey , backendPort string ,
2017-11-07 16:36:51 +00:00
hz * healthcheck . Config ) ( [ ] ingress . Endpoint , error ) {
2017-11-05 01:18:28 +00:00
svc , err := n . listers . Service . GetByName ( svcKey )
2017-09-13 19:15:47 +00:00
var upstreams [ ] ingress . Endpoint
2016-11-10 22:56:29 +00:00
if err != nil {
return upstreams , fmt . Errorf ( "error getting service %v from the cache: %v" , svcKey , err )
}
glog . V ( 3 ) . Infof ( "obtaining port information for service %v" , svcKey )
for _ , servicePort := range svc . Spec . Ports {
// targetPort could be a string, use the name or the port (int)
if strconv . Itoa ( int ( servicePort . Port ) ) == backendPort ||
servicePort . TargetPort . String ( ) == backendPort ||
servicePort . Name == backendPort {
2017-11-05 01:18:28 +00:00
endps := n . getEndpoints ( svc , & servicePort , apiv1 . ProtocolTCP , hz )
2016-11-10 22:56:29 +00:00
if len ( endps ) == 0 {
glog . Warningf ( "service %v does not have any active endpoints" , svcKey )
}
2017-11-05 01:18:28 +00:00
if n . cfg . SortBackends {
2017-09-25 19:40:55 +00:00
sort . SliceStable ( endps , func ( i , j int ) bool {
2017-09-18 23:53:26 +00:00
iName := endps [ i ] . Address
jName := endps [ j ] . Address
if iName != jName {
return iName < jName
}
return endps [ i ] . Port < endps [ j ] . Port
} )
2017-07-13 00:35:36 +00:00
}
2016-11-10 22:56:29 +00:00
upstreams = append ( upstreams , endps ... )
break
}
}
2017-10-26 22:17:18 +00:00
// Ingress with an ExternalName service and no port defined in the service.
if len ( svc . Spec . Ports ) == 0 && svc . Spec . Type == apiv1 . ServiceTypeExternalName {
externalPort , err := strconv . Atoi ( backendPort )
if err != nil {
glog . Warningf ( "only numeric ports are allowed in ExternalName services: %v is not valid as a TCP/UDP port" , backendPort )
return upstreams , nil
}
servicePort := apiv1 . ServicePort {
Protocol : "TCP" ,
Port : int32 ( externalPort ) ,
TargetPort : intstr . FromString ( backendPort ) ,
}
2017-11-05 01:18:28 +00:00
endps := n . getEndpoints ( svc , & servicePort , apiv1 . ProtocolTCP , hz )
2017-10-26 22:17:18 +00:00
if len ( endps ) == 0 {
glog . Warningf ( "service %v does not have any active endpoints" , svcKey )
return upstreams , nil
}
upstreams = append ( upstreams , endps ... )
return upstreams , nil
}
2017-11-05 01:18:28 +00:00
if ! n . cfg . SortBackends {
2017-07-13 00:35:36 +00:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
for i := range upstreams {
j := rand . Intn ( i + 1 )
upstreams [ i ] , upstreams [ j ] = upstreams [ j ] , upstreams [ i ]
}
2017-06-14 21:33:12 +00:00
}
2016-11-10 22:56:29 +00:00
return upstreams , nil
}
2017-02-16 17:26:58 +00:00
// createServers initializes a map that contains information about the list of
// FDQN referenced by ingress rules and the common name field in the referenced
// SSL certificates. Each server is configured with location / using a default
// backend specified by the user or the one inside the ingress spec.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) createServers ( data [ ] * extensions . Ingress ,
2017-09-18 23:53:26 +00:00
upstreams map [ string ] * ingress . Backend ,
du * ingress . Backend ) map [ string ] * ingress . Server {
servers := make ( map [ string ] * ingress . Server , len ( data ) )
// If a server has a hostname equivalent to a pre-existing alias, then we
// remove the alias to avoid conflicts.
aliases := make ( map [ string ] string , len ( data ) )
2017-09-13 19:15:47 +00:00
2017-11-05 01:18:28 +00:00
bdef := n . GetDefaultBackend ( )
2017-11-07 16:36:51 +00:00
ngxProxy := proxy . Config {
2017-11-13 22:57:41 +00:00
BodySize : bdef . ProxyBodySize ,
ConnectTimeout : bdef . ProxyConnectTimeout ,
SendTimeout : bdef . ProxySendTimeout ,
ReadTimeout : bdef . ProxyReadTimeout ,
BufferSize : bdef . ProxyBufferSize ,
CookieDomain : bdef . ProxyCookieDomain ,
CookiePath : bdef . ProxyCookiePath ,
NextUpstream : bdef . ProxyNextUpstream ,
RequestBuffering : bdef . ProxyRequestBuffering ,
ProxyRedirectFrom : bdef . ProxyRedirectFrom ,
2016-12-29 20:02:06 +00:00
}
2017-09-27 03:46:22 +00:00
// generated on Start() with createDefaultSSLCertificate()
2017-11-05 01:18:28 +00:00
defaultPemFileName := n . cfg . FakeCertificatePath
defaultPemSHA := n . cfg . FakeCertificateSHA
2017-03-06 19:29:33 +00:00
2017-09-27 03:46:22 +00:00
// Tries to fetch the default Certificate from nginx configuration.
// If it does not exists, use the ones generated on Start()
2017-11-05 01:18:28 +00:00
defaultCertificate , err := n . getPemCertificate ( n . cfg . DefaultSSLCertificate )
2017-08-26 03:46:17 +00:00
if err == nil {
2017-01-26 18:51:55 +00:00
defaultPemFileName = defaultCertificate . PemFileName
defaultPemSHA = defaultCertificate . PemSHA
}
2017-02-16 17:26:58 +00:00
// initialize the default server
2016-11-10 22:56:29 +00:00
servers [ defServerName ] = & ingress . Server {
2017-01-26 18:51:55 +00:00
Hostname : defServerName ,
SSLCertificate : defaultPemFileName ,
SSLPemChecksum : defaultPemSHA ,
2016-11-10 22:56:29 +00:00
Locations : [ ] * ingress . Location {
{
Path : rootLocation ,
IsDefBackend : true ,
2017-08-11 02:51:32 +00:00
Backend : du . Name ,
2016-11-10 22:56:29 +00:00
Proxy : ngxProxy ,
2017-08-11 02:51:32 +00:00
Service : du . Service ,
2016-11-10 22:56:29 +00:00
} ,
2017-09-13 19:15:47 +00:00
} }
2016-11-10 22:56:29 +00:00
// initialize all the servers
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2017-11-07 16:36:51 +00:00
anns := n . getIngressAnnotations ( ing )
2017-09-17 14:54:00 +00:00
// default upstream server
2017-08-11 02:51:32 +00:00
un := du . Name
2017-09-17 14:54:00 +00:00
2017-02-16 17:26:58 +00:00
if ing . Spec . Backend != nil {
// replace default backend
2017-09-13 19:15:47 +00:00
defUpstream := fmt . Sprintf ( "%v-%v-%v" , ing . GetNamespace ( ) , ing . Spec . Backend . ServiceName , ing . Spec . Backend . ServicePort . String ( ) )
2017-02-16 17:26:58 +00:00
if backendUpstream , ok := upstreams [ defUpstream ] ; ok {
2017-08-11 02:51:32 +00:00
un = backendUpstream . Name
2017-09-17 14:54:00 +00:00
// Special case:
// ingress only with a backend and no rules
// this case defines a "catch all" server
defLoc := servers [ defServerName ] . Locations [ 0 ]
if defLoc . IsDefBackend && len ( ing . Spec . Rules ) == 0 {
defLoc . IsDefBackend = false
defLoc . Backend = backendUpstream . Name
defLoc . Service = backendUpstream . Service
}
2017-02-16 17:26:58 +00:00
}
}
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
if _ , ok := servers [ host ] ; ok {
// server already configured
continue
}
2017-04-09 23:51:38 +00:00
2016-11-10 22:56:29 +00:00
servers [ host ] = & ingress . Server {
2016-11-16 18:24:26 +00:00
Hostname : host ,
2016-11-10 22:56:29 +00:00
Locations : [ ] * ingress . Location {
{
2017-08-23 17:39:40 +00:00
Path : rootLocation ,
IsDefBackend : true ,
Backend : un ,
Proxy : ngxProxy ,
2017-09-17 18:42:31 +00:00
Service : & apiv1 . Service { } ,
2016-11-10 22:56:29 +00:00
} ,
2017-09-18 23:53:26 +00:00
} ,
2017-11-07 16:36:51 +00:00
SSLPassthrough : anns . SSLPassthrough ,
2017-09-18 23:53:26 +00:00
}
2016-11-10 22:56:29 +00:00
}
}
2017-08-10 03:22:54 +00:00
// configure default location, alias, and SSL
2017-09-25 19:40:55 +00:00
for _ , ing := range data {
2017-11-07 16:36:51 +00:00
anns := n . getIngressAnnotations ( ing )
2017-08-10 03:22:54 +00:00
2016-11-10 22:56:29 +00:00
for _ , rule := range ing . Spec . Rules {
host := rule . Host
if host == "" {
host = defServerName
}
2017-08-10 03:22:54 +00:00
// setup server aliases
2017-11-07 16:36:51 +00:00
if anns . Alias != "" {
2017-10-27 11:46:32 +00:00
if servers [ host ] . Alias == "" {
2017-11-07 16:36:51 +00:00
servers [ host ] . Alias = anns . Alias
if _ , ok := aliases [ "Alias" ] ; ! ok {
aliases [ "Alias" ] = host
2017-10-27 11:46:32 +00:00
}
} else {
glog . Warningf ( "ingress %v/%v for host %v contains an Alias but one has already been configured." ,
ing . Namespace , ing . Name , host )
2017-09-18 23:53:26 +00:00
}
}
2017-08-10 03:22:54 +00:00
2017-09-27 06:59:10 +00:00
//notifying the user that it has already been configured.
2017-11-07 16:36:51 +00:00
if servers [ host ] . ServerSnippet != "" && anns . ServerSnippet != "" {
2017-09-27 06:59:10 +00:00
glog . Warningf ( "ingress %v/%v for host %v contains a Server Snippet section that it has already been configured." ,
ing . Namespace , ing . Name , host )
}
// only add a server snippet if the server does not have one previously configured
2017-11-07 16:36:51 +00:00
if servers [ host ] . ServerSnippet == "" && anns . ServerSnippet != "" {
servers [ host ] . ServerSnippet = anns . ServerSnippet
2017-09-27 06:59:10 +00:00
}
2016-11-24 00:14:14 +00:00
// only add a certificate if the server does not have one previously configured
2017-08-29 19:40:03 +00:00
if servers [ host ] . SSLCertificate != "" {
continue
}
if len ( ing . Spec . TLS ) == 0 {
glog . V ( 3 ) . Infof ( "ingress %v/%v for host %v does not contains a TLS section" , ing . Namespace , ing . Name , host )
2017-07-12 17:31:15 +00:00
continue
}
2016-12-25 19:48:10 +00:00
2017-07-12 17:31:15 +00:00
tlsSecretName := ""
found := false
for _ , tls := range ing . Spec . TLS {
if sets . NewString ( tls . Hosts ... ) . Has ( host ) {
tlsSecretName = tls . SecretName
found = true
break
2017-03-26 12:28:59 +00:00
}
2017-07-12 17:31:15 +00:00
}
2017-03-26 12:28:59 +00:00
2017-07-12 17:31:15 +00:00
if ! found {
2017-11-10 02:00:38 +00:00
// does not contains a TLS section but none of the host match
2017-07-12 17:31:15 +00:00
continue
}
2017-03-16 15:07:07 +00:00
2017-07-12 17:31:15 +00:00
if tlsSecretName == "" {
2017-07-27 16:57:34 +00:00
glog . V ( 3 ) . Infof ( "host %v is listed on tls section but secretName is empty. Using default cert" , host )
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLCertificate = defaultPemFileName
servers [ host ] . SSLPemChecksum = defaultPemSHA
continue
}
2017-06-12 12:45:08 +00:00
2017-07-12 17:31:15 +00:00
key := fmt . Sprintf ( "%v/%v" , ing . Namespace , tlsSecretName )
2017-11-05 01:18:28 +00:00
bc , exists := n . sslCertTracker . Get ( key )
2017-07-12 17:31:15 +00:00
if ! exists {
2017-08-29 19:40:03 +00:00
glog . Warningf ( "ssl certificate \"%v\" does not exist in local store" , key )
2017-07-12 17:31:15 +00:00
continue
}
2017-05-12 00:50:43 +00:00
2017-07-12 17:31:15 +00:00
cert := bc . ( * ingress . SSLCert )
2017-08-10 03:27:57 +00:00
err = cert . Certificate . VerifyHostname ( host )
if err != nil {
glog . Warningf ( "ssl certificate %v does not contain a Common Name or Subject Alternative Name for host %v" , key , host )
2017-07-12 17:31:15 +00:00
continue
}
2017-05-12 00:50:43 +00:00
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLCertificate = cert . PemFileName
2017-10-04 20:11:03 +00:00
servers [ host ] . SSLFullChainCertificate = cert . FullChainPemFileName
2017-07-12 17:31:15 +00:00
servers [ host ] . SSLPemChecksum = cert . PemSHA
servers [ host ] . SSLExpireTime = cert . ExpireTime
if cert . ExpireTime . Before ( time . Now ( ) . Add ( 240 * time . Hour ) ) {
glog . Warningf ( "ssl certificate for host %v is about to expire in 10 days" , host )
2016-11-10 22:56:29 +00:00
}
}
}
2017-09-18 23:53:26 +00:00
for alias , host := range aliases {
if _ , ok := servers [ alias ] ; ok {
glog . Warningf ( "There is a conflict with server hostname '%v' and alias '%v' (in server %v). Removing alias to avoid conflicts." , alias , host )
servers [ host ] . Alias = ""
}
}
2017-09-20 09:35:16 +00:00
2016-11-10 22:56:29 +00:00
return servers
}
// getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination.
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) getEndpoints (
2017-09-17 18:42:31 +00:00
s * apiv1 . Service ,
servicePort * apiv1 . ServicePort ,
proto apiv1 . Protocol ,
2017-11-07 16:36:51 +00:00
hz * healthcheck . Config ) [ ] ingress . Endpoint {
2016-11-10 22:56:29 +00:00
2016-11-11 23:43:35 +00:00
upsServers := [ ] ingress . Endpoint { }
2016-11-10 22:56:29 +00:00
2017-03-16 11:20:52 +00:00
// avoid duplicated upstream servers when the service
// contains multiple port definitions sharing the same
// targetport.
2017-06-14 23:49:35 +00:00
adus := make ( map [ string ] bool )
2017-03-16 11:20:52 +00:00
2017-04-20 02:30:51 +00:00
// ExternalName services
2017-09-17 18:42:31 +00:00
if s . Spec . Type == apiv1 . ServiceTypeExternalName {
2017-10-26 22:17:18 +00:00
glog . V ( 3 ) . Info ( "Ingress using a service %v of type=ExternalName : %v" , s . Name )
2017-06-13 03:02:28 +00:00
targetPort := servicePort . TargetPort . IntValue ( )
2017-04-20 02:30:51 +00:00
// check for invalid port value
if targetPort <= 0 {
2017-10-26 22:17:18 +00:00
glog . Errorf ( "ExternalName service with an invalid port: %v" , targetPort )
2017-04-20 02:30:51 +00:00
return upsServers
}
2017-09-24 20:36:59 +00:00
if net . ParseIP ( s . Spec . ExternalName ) == nil {
_ , err := net . LookupHost ( s . Spec . ExternalName )
if err != nil {
glog . Errorf ( "unexpected error resolving host %v: %v" , s . Spec . ExternalName , err )
return upsServers
}
}
2017-04-20 02:30:51 +00:00
return append ( upsServers , ingress . Endpoint {
Address : s . Spec . ExternalName ,
Port : fmt . Sprintf ( "%v" , targetPort ) ,
MaxFails : hz . MaxFails ,
FailTimeout : hz . FailTimeout ,
} )
}
glog . V ( 3 ) . Infof ( "getting endpoints for service %v/%v and port %v" , s . Namespace , s . Name , servicePort . String ( ) )
2017-11-05 01:18:28 +00:00
ep , err := n . listers . Endpoint . GetServiceEndpoints ( s )
2017-04-20 02:30:51 +00:00
if err != nil {
glog . Warningf ( "unexpected error obtaining service endpoints: %v" , err )
return upsServers
}
2016-11-10 22:56:29 +00:00
for _ , ss := range ep . Subsets {
for _ , epPort := range ss . Ports {
if ! reflect . DeepEqual ( epPort . Protocol , proto ) {
continue
}
var targetPort int32
2017-06-13 03:02:28 +00:00
if servicePort . Name == "" {
// ServicePort.Name is optional if there is only one port
targetPort = epPort . Port
} else if servicePort . Name == epPort . Name {
targetPort = epPort . Port
2016-11-10 22:56:29 +00:00
}
// check for invalid port value
2016-12-29 22:59:56 +00:00
if targetPort <= 0 {
2016-11-10 22:56:29 +00:00
continue
}
for _ , epAddress := range ss . Addresses {
2017-03-16 11:20:52 +00:00
ep := fmt . Sprintf ( "%v:%v" , epAddress . IP , targetPort )
if _ , exists := adus [ ep ] ; exists {
continue
}
2016-11-11 23:43:35 +00:00
ups := ingress . Endpoint {
2016-11-10 22:56:29 +00:00
Address : epAddress . IP ,
Port : fmt . Sprintf ( "%v" , targetPort ) ,
MaxFails : hz . MaxFails ,
FailTimeout : hz . FailTimeout ,
2017-07-29 13:27:56 +00:00
Target : epAddress . TargetRef ,
2016-11-10 22:56:29 +00:00
}
upsServers = append ( upsServers , ups )
2017-03-16 11:20:52 +00:00
adus [ ep ] = true
2016-11-10 22:56:29 +00:00
}
}
}
glog . V ( 3 ) . Infof ( "endpoints found: %v" , upsServers )
return upsServers
}
2017-07-27 17:46:33 +00:00
// readSecrets extracts information about secrets from an Ingress rule
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) readSecrets ( ing * extensions . Ingress ) {
2017-04-09 16:52:10 +00:00
for _ , tls := range ing . Spec . TLS {
2017-05-05 15:28:01 +00:00
if tls . SecretName == "" {
continue
}
2017-04-09 16:52:10 +00:00
key := fmt . Sprintf ( "%v/%v" , ing . Namespace , tls . SecretName )
2017-11-05 01:18:28 +00:00
n . syncSecret ( key )
2017-07-27 17:46:33 +00:00
}
2017-11-08 20:58:57 +00:00
key , _ := parser . GetStringAnnotation ( "auth-tls-secret" , ing , n )
2017-07-27 17:46:33 +00:00
if key == "" {
return
2017-04-09 16:52:10 +00:00
}
2017-11-05 01:18:28 +00:00
n . syncSecret ( key )
2016-11-10 22:56:29 +00:00
}
2017-08-26 03:46:17 +00:00
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) isForceReload ( ) bool {
return atomic . LoadInt32 ( & n . forceReload ) != 0
2017-09-19 20:09:33 +00:00
}
2017-11-07 16:36:51 +00:00
// SetForceReload sets if the ingress controller should be reloaded or not
2017-11-05 01:18:28 +00:00
func ( n * NGINXController ) SetForceReload ( shouldReload bool ) {
2017-09-19 20:09:33 +00:00
if shouldReload {
2017-11-05 01:18:28 +00:00
atomic . StoreInt32 ( & n . forceReload , 1 )
n . syncQueue . Enqueue ( & extensions . Ingress { } )
2017-09-19 20:09:33 +00:00
} else {
2017-11-05 01:18:28 +00:00
atomic . StoreInt32 ( & n . forceReload , 0 )
2017-08-26 03:46:17 +00:00
}
}
2017-11-07 16:36:51 +00:00
func ( n * NGINXController ) extractAnnotations ( ing * extensions . Ingress ) {
anns := n . annotations . Extract ( ing )
glog . V ( 3 ) . Infof ( "updating annotations information for ingres %v/%v" , anns . Namespace , anns . Name )
n . listers . IngressAnnotation . Update ( anns )
}
// getByIngress returns the parsed annotations from an Ingress
func ( n * NGINXController ) getIngressAnnotations ( ing * extensions . Ingress ) * annotations . Ingress {
key := fmt . Sprintf ( "%v/%v" , ing . Namespace , ing . Name )
item , exists , err := n . listers . IngressAnnotation . GetByKey ( key )
if err != nil {
glog . Errorf ( "unexpected error getting ingress annotation %v: %v" , key , err )
return & annotations . Ingress { }
}
if ! exists {
glog . Errorf ( "ingress annotation %v was not found" , key )
return & annotations . Ingress { }
}
return item . ( * annotations . Ingress )
}