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 }