Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync SSL certificates on events #2342

Merged
merged 3 commits into from
Apr 13, 2018
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
15 changes: 3 additions & 12 deletions internal/ingress/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/proxy"
ngx_config "k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/task"
)

const (
Expand Down Expand Up @@ -114,21 +113,13 @@ func (n NGINXController) GetPublishService() *apiv1.Service {
// 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.
func (n *NGINXController) syncIngress(item interface{}) error {
func (n *NGINXController) syncIngress(interface{}) error {
n.syncRateLimiter.Accept()

if n.syncQueue.IsShuttingDown() {
return nil
}

if element, ok := item.(task.Element); ok {
if name, ok := element.Key.(string); ok {
if ing, err := n.store.GetIngress(name); err == nil {
n.store.ReadSecrets(ing)
}
}
}

// Sort ingress rules using the ResourceVersion field
ings := n.store.ListIngresses()
sort.SliceStable(ings, func(i, j int) bool {
Expand Down Expand Up @@ -869,7 +860,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,

// Tries to fetch the default Certificate from nginx configuration.
// If it does not exists, use the ones generated on Start()
defaultCertificate, err := n.store.GetLocalSecret(n.cfg.DefaultSSLCertificate)
defaultCertificate, err := n.store.GetLocalSSLCert(n.cfg.DefaultSSLCertificate)
if err == nil {
defaultPemFileName = defaultCertificate.PemFileName
defaultPemSHA = defaultCertificate.PemSHA
Expand Down Expand Up @@ -1039,7 +1030,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
}

key := fmt.Sprintf("%v/%v", ing.Namespace, tlsSecretName)
cert, err := n.store.GetLocalSecret(key)
cert, err := n.store.GetLocalSSLCert(key)
if err != nil {
glog.Warningf("ssl certificate \"%v\" does not exist in local store", key)
continue
Expand Down
51 changes: 3 additions & 48 deletions internal/ingress/controller/store/backend_ssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (

"k8s.io/ingress-nginx/internal/file"
"k8s.io/ingress-nginx/internal/ingress"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/net/ssl"
)
Expand All @@ -51,7 +50,7 @@ func (s k8sStore) syncSecret(key string) {
}

// create certificates and add or update the item in the store
cur, err := s.GetLocalSecret(key)
cur, err := s.GetLocalSSLCert(key)
if err == nil {
if cur.Equal(cert) {
// no need to update
Expand Down Expand Up @@ -129,9 +128,9 @@ func (s k8sStore) getPemCertificate(secretName string) (*ingress.SSLCert, error)
}

func (s k8sStore) checkSSLChainIssues() {
for _, item := range s.ListLocalSecrets() {
for _, item := range s.ListLocalSSLCerts() {
secretName := k8s.MetaNamespaceKey(item)
secret, err := s.GetLocalSecret(secretName)
secret, err := s.GetLocalSSLCert(secretName)
if err != nil {
continue
}
Expand Down Expand Up @@ -179,50 +178,6 @@ func (s k8sStore) checkSSLChainIssues() {
}
}

// checkMissingSecrets verifies if one or more ingress rules contains
// a reference to a secret that is not present in the local secret store.
func (s k8sStore) checkMissingSecrets() {
for _, ing := range s.ListIngresses() {
for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" {
continue
}

key := fmt.Sprintf("%v/%v", ing.Namespace, tls.SecretName)
if _, ok := s.sslStore.Get(key); !ok {
s.syncSecret(key)
}
}

key, _ := parser.GetStringAnnotation("auth-tls-secret", ing)
if key == "" {
continue
}

if _, ok := s.sslStore.Get(key); !ok {
s.syncSecret(key)
}
}
}

// ReadSecrets extracts information about secrets from an Ingress rule
func (s k8sStore) ReadSecrets(ing *extensions.Ingress) {
for _, tls := range ing.Spec.TLS {
if tls.SecretName == "" {
continue
}

key := fmt.Sprintf("%v/%v", ing.Namespace, tls.SecretName)
s.syncSecret(key)
}

key, _ := parser.GetStringAnnotation("auth-tls-secret", ing)
if key == "" {
return
}
s.syncSecret(key)
}

// sendDummyEvent sends a dummy event to trigger an update
// This is used in when a secret change
func (s *k8sStore) sendDummyEvent() {
Expand Down
130 changes: 130 additions & 0 deletions internal/ingress/controller/store/objectref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
Copyright 2018 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 store

import (
"sync"

"k8s.io/apimachinery/pkg/util/sets"
)

// ObjectRefMap is a map of references from object(s) to object (1:n). It is
// used to keep track of which data objects (Secrets) are used within Ingress
// objects.
type ObjectRefMap interface {
Insert(consumer string, ref ...string)
Delete(consumer string)
Len() int
Has(ref string) bool
HasConsumer(consumer string) bool
Reference(ref string) []string
ReferencedBy(consumer string) []string
}

type objectRefMap struct {
sync.Mutex
v map[string]sets.String
}

// NewObjectRefMap returns a new ObjectRefMap.
func NewObjectRefMap() ObjectRefMap {
return &objectRefMap{
v: make(map[string]sets.String),
}
}

// Insert adds a consumer to one or more referenced objects.
func (o *objectRefMap) Insert(consumer string, ref ...string) {
o.Lock()
defer o.Unlock()

for _, r := range ref {
if _, ok := o.v[r]; !ok {
o.v[r] = sets.NewString(consumer)
continue
}
o.v[r].Insert(consumer)
}
}

// Delete deletes a consumer from all referenced objects.
func (o *objectRefMap) Delete(consumer string) {
o.Lock()
defer o.Unlock()

for ref, consumers := range o.v {
consumers.Delete(consumer)
if consumers.Len() == 0 {
delete(o.v, ref)
}
}
}

// Len returns the count of referenced objects.
func (o *objectRefMap) Len() int {
return len(o.v)
}

// Has returns whether the given object is referenced by any other object.
func (o *objectRefMap) Has(ref string) bool {
o.Lock()
defer o.Unlock()

if _, ok := o.v[ref]; ok {
return true
}
return false
}

// HasConsumer returns whether the store contains the given consumer.
func (o *objectRefMap) HasConsumer(consumer string) bool {
o.Lock()
defer o.Unlock()

for _, consumers := range o.v {
if consumers.Has(consumer) {
return true
}
}
return false
}

// Reference returns all objects referencing the given object.
func (o *objectRefMap) Reference(ref string) []string {
o.Lock()
defer o.Unlock()

consumers, ok := o.v[ref]
if !ok {
return make([]string, 0)
}
return consumers.List()
}

// ReferencedBy returns all objects referenced by the given object.
func (o *objectRefMap) ReferencedBy(consumer string) []string {
o.Lock()
defer o.Unlock()

refs := make([]string, 0)
for ref, consumers := range o.v {
if consumers.Has(consumer) {
refs = append(refs, ref)
}
}
return refs
}
65 changes: 65 additions & 0 deletions internal/ingress/controller/store/objectref_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
Copyright 2018 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 store

import "testing"

func TestObjectRefMapOperations(t *testing.T) {
orm := NewObjectRefMap()

items := []struct {
consumer string
ref []string
}{
{"ns/ingress1", []string{"ns/tls1"}},
{"ns/ingress2", []string{"ns/tls1", "ns/tls2"}},
{"ns/ingress3", []string{"ns/tls1", "ns/tls2", "ns/tls3"}},
}

// populate map with test data
for _, i := range items {
orm.Insert(i.consumer, i.ref...)
}
if l := orm.Len(); l != 3 {
t.Fatalf("Expected 3 referenced objects (got %d)", l)
}

// add already existing item
orm.Insert("ns/ingress1", "ns/tls1")
if l := len(orm.ReferencedBy("ns/ingress1")); l != 1 {
t.Error("Expected existing item not to be added again")
}

// find consumer by name
if !orm.HasConsumer("ns/ingress1") {
t.Error("Expected the \"ns/ingress1\" consumer to exist in the map")
}

// count references to object
if l := len(orm.Reference("ns/tls1")); l != 3 {
t.Errorf("Expected \"ns/tls1\" to be referenced by 3 objects (got %d)", l)
}

// delete consumer
orm.Delete("ns/ingress3")
if l := orm.Len(); l != 2 {
t.Errorf("Expected 2 referenced objects (got %d)", l)
}
if orm.Has("ns/tls3") {
t.Error("Expected \"ns/tls3\" not to be referenced")
}
}
Loading