sigs.k8s.io/external-dns@v0.14.1/endpoint/labels.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package endpoint
    18  
    19  import (
    20  	log "github.com/sirupsen/logrus"
    21  
    22  	"errors"
    23  	"fmt"
    24  	"sort"
    25  	"strings"
    26  )
    27  
    28  // ErrInvalidHeritage is returned when heritage was not found, or different heritage is found
    29  var ErrInvalidHeritage = errors.New("heritage is unknown or not found")
    30  
    31  const (
    32  	heritage = "external-dns"
    33  	// OwnerLabelKey is the name of the label that defines the owner of an Endpoint.
    34  	OwnerLabelKey = "owner"
    35  	// ResourceLabelKey is the name of the label that identifies k8s resource which wants to acquire the DNS name
    36  	ResourceLabelKey = "resource"
    37  	// OwnedRecordLabelKey is the name of the label that identifies the record that is owned by the labeled TXT registry record
    38  	OwnedRecordLabelKey = "ownedRecord"
    39  
    40  	// AWSSDDescriptionLabel label responsible for storing raw owner/resource combination information in the Labels
    41  	// supposed to be inserted by AWS SD Provider, and parsed into OwnerLabelKey and ResourceLabelKey key by AWS SD Registry
    42  	AWSSDDescriptionLabel = "aws-sd-description"
    43  
    44  	// DualstackLabelKey is the name of the label that identifies dualstack endpoints
    45  	DualstackLabelKey = "dualstack"
    46  
    47  	// txtEncryptionNonce label for keep same nonce for same txt records, for prevent different result of encryption for same txt record, it can cause issues for some providers
    48  	txtEncryptionNonce = "txt-encryption-nonce"
    49  )
    50  
    51  // Labels store metadata related to the endpoint
    52  // it is then stored in a persistent storage via serialization
    53  type Labels map[string]string
    54  
    55  // NewLabels returns empty Labels
    56  func NewLabels() Labels {
    57  	return map[string]string{}
    58  }
    59  
    60  // NewLabelsFromString constructs endpoints labels from a provided format string
    61  // if heritage set to another value is found then error is returned
    62  // no heritage automatically assumes is not owned by external-dns and returns invalidHeritage error
    63  func NewLabelsFromStringPlain(labelText string) (Labels, error) {
    64  	endpointLabels := map[string]string{}
    65  	labelText = strings.Trim(labelText, "\"") // drop quotes
    66  	tokens := strings.Split(labelText, ",")
    67  	foundExternalDNSHeritage := false
    68  	for _, token := range tokens {
    69  		if len(strings.Split(token, "=")) != 2 {
    70  			continue
    71  		}
    72  		key := strings.Split(token, "=")[0]
    73  		val := strings.Split(token, "=")[1]
    74  		if key == "heritage" && val != heritage {
    75  			return nil, ErrInvalidHeritage
    76  		}
    77  		if key == "heritage" {
    78  			foundExternalDNSHeritage = true
    79  			continue
    80  		}
    81  		if strings.HasPrefix(key, heritage) {
    82  			endpointLabels[strings.TrimPrefix(key, heritage+"/")] = val
    83  		}
    84  	}
    85  
    86  	if !foundExternalDNSHeritage {
    87  		return nil, ErrInvalidHeritage
    88  	}
    89  
    90  	return endpointLabels, nil
    91  }
    92  
    93  func NewLabelsFromString(labelText string, aesKey []byte) (Labels, error) {
    94  	if len(aesKey) != 0 {
    95  		decryptedText, encryptionNonce, err := DecryptText(strings.Trim(labelText, "\""), aesKey)
    96  		//in case if we have decryption error, just try process original text
    97  		//decryption errors should be ignored here, because we can already have plain-text labels in registry
    98  		if err == nil {
    99  			labels, err := NewLabelsFromStringPlain(decryptedText)
   100  			if err == nil {
   101  				labels[txtEncryptionNonce] = encryptionNonce
   102  			}
   103  
   104  			return labels, err
   105  		}
   106  	}
   107  	return NewLabelsFromStringPlain(labelText)
   108  }
   109  
   110  // SerializePlain transforms endpoints labels into a external-dns recognizable format string
   111  // withQuotes adds additional quotes
   112  func (l Labels) SerializePlain(withQuotes bool) string {
   113  	var tokens []string
   114  	tokens = append(tokens, fmt.Sprintf("heritage=%s", heritage))
   115  	var keys []string
   116  	for key := range l {
   117  		keys = append(keys, key)
   118  	}
   119  	sort.Strings(keys) // sort for consistency
   120  
   121  	for _, key := range keys {
   122  		if key == txtEncryptionNonce {
   123  			continue
   124  		}
   125  		tokens = append(tokens, fmt.Sprintf("%s/%s=%s", heritage, key, l[key]))
   126  	}
   127  	if withQuotes {
   128  		return fmt.Sprintf("\"%s\"", strings.Join(tokens, ","))
   129  	}
   130  	return strings.Join(tokens, ",")
   131  }
   132  
   133  // Serialize same to SerializePlain, but encrypt data, if encryption enabled
   134  func (l Labels) Serialize(withQuotes bool, txtEncryptEnabled bool, aesKey []byte) string {
   135  	if !txtEncryptEnabled {
   136  		return l.SerializePlain(withQuotes)
   137  	}
   138  
   139  	var encryptionNonce []byte
   140  	if extractedNonce, nonceExists := l[txtEncryptionNonce]; nonceExists {
   141  		encryptionNonce = []byte(extractedNonce)
   142  	} else {
   143  		var err error
   144  		encryptionNonce, err = GenerateNonce()
   145  		if err != nil {
   146  			log.Fatalf("Failed to generate cryptographic nonce %#v.", err)
   147  		}
   148  		l[txtEncryptionNonce] = string(encryptionNonce)
   149  	}
   150  
   151  	text := l.SerializePlain(false)
   152  	log.Debugf("Encrypt the serialized text %#v before returning it.", text)
   153  	var err error
   154  	text, err = EncryptText(text, aesKey, encryptionNonce)
   155  
   156  	if err != nil {
   157  		log.Fatalf("Failed to encrypt the text %#v using the encryption key %#v. Got error %#v.", text, aesKey, err)
   158  	}
   159  
   160  	if withQuotes {
   161  		text = fmt.Sprintf("\"%s\"", text)
   162  	}
   163  	log.Debugf("Serialized text after encryption is %#v.", text)
   164  	return text
   165  }