github.com/caos/orbos@v1.5.14-0.20221103111702-e6cd0cea7ad4/pkg/secret/secret.go (about) 1 package secret 2 3 import ( 4 "crypto/aes" 5 "crypto/cipher" 6 "crypto/rand" 7 "encoding/base64" 8 "errors" 9 "io" 10 "strings" 11 "unicode/utf8" 12 13 "github.com/caos/orbos/internal/utils/clientgo" 14 "gopkg.in/yaml.v3" 15 ) 16 17 var Masterkey = "empty" 18 19 // Secret: Secret handled with orbctl so no manual changes are required 20 type Secret struct { 21 //Encryption algorithm used for the secret 22 Encryption string `json:"encryption,omitempty" yaml:"encryption,omitempty"` 23 //Encoding algorithm used for the secret 24 Encoding string `json:"encoding,omitempty" yaml:"encoding,omitempty"` 25 //Encrypted and encoded Value 26 Value string `json:"value,omitempty" yaml:"value,omitempty"` 27 } 28 type secretAlias Secret 29 30 const existingSecretsNamespace = "caos-system" 31 32 // Existing: Used secret that has to be already existing in the cluster 33 type Existing struct { 34 //Name of the Secret 35 Name string `json:"name" yaml:"name"` 36 //Key in the secret from where the value should be used 37 Key string `json:"key" yaml:"key"` 38 //Name which should be used internally, should be unique for the volume and volumemounts 39 InternalName string `json:"internalName,omitempty" yaml:"internalName,omitempty"` 40 } 41 42 func (s *Existing) IsZero() bool { 43 if s.Name == "" && s.Key == "" { 44 return true 45 } 46 return false 47 } 48 49 // Existing: Used secret that has to be already existing in the cluster and should contain id/username and secret/password 50 type ExistingIDSecret struct { 51 //Name of the Secret 52 Name string `json:"name" yaml:"name"` 53 //Key in the secret which contains the ID 54 IDKey string `json:"idKey" yaml:"idKey"` 55 //Key in the secret which contains the secret 56 SecretKey string `json:"secretKey" yaml:"secretKey"` 57 //Name which should be used internally, should be unique for the volume and volumemounts 58 InternalName string `json:"internalName,omitempty" yaml:"internalName,omitempty"` 59 } 60 61 func (s *Secret) IsZero() bool { 62 if s.Value == "" { 63 return true 64 } 65 return false 66 } 67 68 func (s *Secret) UnmarshalYAMLWithExisting(node *yaml.Node, existing *Existing) error { 69 if err := s.UnmarshalYAML(node); err != nil { 70 return err 71 } 72 73 if s.Value == "" { 74 if existing != nil && existing.Name != "" && existing.Key != "" { 75 secret, err := clientgo.GetSecret(existing.Name, "caos-system") 76 if err != nil { 77 return errors.New("Error while reading existing secret") 78 } 79 80 value, found := secret.Data[existing.Key] 81 if !found { 82 return errors.New("Error while reading existing secret, key non-existent") 83 } 84 s.Value = string(value) 85 } 86 } 87 88 return nil 89 } 90 91 func unmarshal(s *Secret) (string, error) { 92 if s.Value == "" { 93 return "", nil 94 } 95 96 cipherText, err := base64.URLEncoding.DecodeString(s.Value) 97 if err != nil { 98 return "", err 99 } 100 101 if len(Masterkey) < 1 || len(Masterkey) > 32 { 102 return "", nil 103 //return errors.New("Master key size must be between 1 and 32 characters") 104 } 105 106 masterKeyLocal := make([]byte, 32) 107 for idx, char := range []byte(strings.Trim(Masterkey, "\n")) { 108 masterKeyLocal[idx] = char 109 } 110 111 block, err := aes.NewCipher(masterKeyLocal) 112 if err != nil { 113 return "", err 114 } 115 116 if len(cipherText) < aes.BlockSize { 117 return "", errors.New("Ciphertext block size is too short") 118 } 119 120 //IV needs to be unique, but doesn't have to be secure. 121 //It's common to put it at the beginning of the ciphertext. 122 iv := cipherText[:aes.BlockSize] 123 cipherText = cipherText[aes.BlockSize:] 124 125 stream := cipher.NewCFBDecrypter(block, iv) 126 // XORKeyStream can work in-place if the two arguments are the same. 127 stream.XORKeyStream(cipherText, cipherText) 128 129 if !utf8.Valid(cipherText) { 130 return "", errors.New("Decryption failed") 131 } 132 // s.monitor.Info("Decoded and decrypted secret") 133 return string(cipherText), nil 134 } 135 136 func (s *Secret) Unmarshal(masterkey string) error { 137 Masterkey = masterkey 138 139 unm, err := unmarshal(s) 140 if err != nil { 141 return err 142 } 143 144 s.Value = unm 145 return nil 146 } 147 148 func (s *Secret) UnmarshalYAML(node *yaml.Node) error { 149 alias := new(secretAlias) 150 err := node.Decode(alias) 151 152 s.Encoding = alias.Encoding 153 s.Encryption = alias.Encryption 154 s.Value = alias.Value 155 156 if alias.Value == "" { 157 return nil 158 } 159 160 if len(Masterkey) < 1 || len(Masterkey) > 32 { 161 return nil 162 //return errors.New("Master key size must be between 1 and 32 characters") 163 } 164 165 unmarshalled, err := unmarshal(s) 166 if err != nil { 167 return err 168 } 169 170 // s.monitor.Info("Decoded and decrypted secret") 171 s.Encoding = alias.Encoding 172 s.Encryption = alias.Encryption 173 s.Value = unmarshalled 174 return nil 175 } 176 177 func (s *Secret) MarshalYAML() (interface{}, error) { 178 179 if s.Value == "" { 180 return nil, nil 181 } 182 183 if len(Masterkey) < 1 || len(Masterkey) > 32 { 184 return nil, errors.New("Master key size must be between 1 and 32 characters") 185 } 186 187 masterKey := make([]byte, 32) 188 for idx, char := range []byte(strings.Trim(Masterkey, "\n")) { 189 masterKey[idx] = char 190 } 191 192 c, err := aes.NewCipher(masterKey) 193 if err != nil { 194 return nil, err 195 } 196 197 cipherText := make([]byte, aes.BlockSize+len(s.Value)) 198 iv := cipherText[:aes.BlockSize] 199 if _, err = io.ReadFull(rand.Reader, iv); err != nil { 200 return nil, err 201 } 202 203 stream := cipher.NewCFBEncrypter(c, iv) 204 stream.XORKeyStream(cipherText[aes.BlockSize:], []byte(s.Value)) 205 206 return &secretAlias{Encryption: "AES256", Encoding: "Base64", Value: base64.URLEncoding.EncodeToString(cipherText)}, nil 207 } 208 209 func InitIfNil(sec *Secret) *Secret { 210 if sec == nil { 211 return &Secret{} 212 } 213 return sec 214 } 215 216 func AppendSecrets(prefix string, intoSecrets, addSecrets map[string]*Secret, intoExisting, addExisting map[string]*Existing) { 217 for key, secret := range addSecrets { 218 name := key 219 if prefix != "" { 220 name = prefix + "." + name 221 } 222 intoSecrets[name] = secret 223 } 224 for key, existing := range addExisting { 225 name := key 226 if prefix != "" { 227 name = prefix + "." + name 228 } 229 intoExisting[name] = existing 230 } 231 } 232 233 func ValidateSecret(secret *Secret, existing *Existing) error { 234 if secret == nil || existing == nil { 235 return errors.New("secret not specified") 236 } 237 238 if secret.Value == "" && (existing.Name == "" || existing.Key == "") { 239 return errors.New("secret has no encrypted value or no valid reference") 240 } 241 return nil 242 }