github.com/argoproj/argo-cd/v2@v2.10.5/util/db/gpgkeys.go (about) 1 package db 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 8 log "github.com/sirupsen/logrus" 9 10 "github.com/argoproj/argo-cd/v2/common" 11 appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 12 "github.com/argoproj/argo-cd/v2/util/gpg" 13 ) 14 15 // Validates a single GnuPG key and returns the key's ID 16 func validatePGPKey(keyData string) (*appsv1.GnuPGPublicKey, error) { 17 f, err := os.CreateTemp("", "gpg-public-key") 18 if err != nil { 19 return nil, err 20 } 21 defer os.Remove(f.Name()) 22 23 err = os.WriteFile(f.Name(), []byte(keyData), 0600) 24 if err != nil { 25 return nil, err 26 } 27 defer func() { 28 err = f.Close() 29 if err != nil { 30 log.Errorf("error closing file %q: %v", f.Name(), err) 31 } 32 }() 33 34 parsed, err := gpg.ValidatePGPKeys(f.Name()) 35 if err != nil { 36 return nil, err 37 } 38 39 // Each key/value pair in the config map must exactly contain one public key, with the (short) GPG key ID as key 40 if len(parsed) != 1 { 41 return nil, fmt.Errorf("More than one key found in input data") 42 } 43 44 var retKey *appsv1.GnuPGPublicKey = nil 45 // Is there a better way to get the first element from a map without knowing its key? 46 for _, k := range parsed { 47 retKey = k 48 break 49 } 50 if retKey != nil { 51 retKey.KeyData = keyData 52 return retKey, nil 53 } else { 54 return nil, fmt.Errorf("Could not find the GPG key") 55 } 56 } 57 58 // ListConfiguredGPGPublicKeys returns a list of all configured GPG public keys from the ConfigMap 59 func (db *db) ListConfiguredGPGPublicKeys(ctx context.Context) (map[string]*appsv1.GnuPGPublicKey, error) { 60 log.Debugf("Loading PGP public keys from config map") 61 result := make(map[string]*appsv1.GnuPGPublicKey) 62 keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName) 63 if err != nil { 64 return nil, err 65 } 66 67 // We have to verify all PGP keys in the ConfigMap to be valid keys before. To do so, 68 // we write each single one out to a temporary file and validate them through gpg. 69 // This is not optimal, but the executil from argo-pkg does not support writing to 70 // stdin of the forked process. So for now, we must live with that. 71 for k, p := range keysCM.Data { 72 if expectedKeyID := gpg.KeyID(k); expectedKeyID != "" { 73 parsedKey, err := validatePGPKey(p) 74 if err != nil { 75 return nil, fmt.Errorf("Could not parse GPG key for entry '%s': %s", expectedKeyID, err.Error()) 76 } 77 if expectedKeyID != parsedKey.KeyID { 78 return nil, fmt.Errorf("Key parsed for entry with key ID '%s' had different key ID '%s'", expectedKeyID, parsedKey.KeyID) 79 } 80 result[parsedKey.KeyID] = parsedKey 81 } else { 82 return nil, fmt.Errorf("Found entry with key '%s' in ConfigMap, but this is not a valid PGP key ID", k) 83 } 84 } 85 86 return result, nil 87 } 88 89 // AddGPGPublicKey adds one or more public keys to the configuration 90 func (db *db) AddGPGPublicKey(ctx context.Context, keyData string) (map[string]*appsv1.GnuPGPublicKey, []string, error) { 91 result := make(map[string]*appsv1.GnuPGPublicKey) 92 skipped := make([]string, 0) 93 94 keys, err := gpg.ValidatePGPKeysFromString(keyData) 95 if err != nil { 96 return nil, nil, err 97 } 98 99 keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName) 100 if err != nil { 101 return nil, nil, err 102 } 103 104 if keysCM.Data == nil { 105 keysCM.Data = make(map[string]string) 106 } 107 108 for kid, key := range keys { 109 if _, ok := keysCM.Data[kid]; ok { 110 skipped = append(skipped, kid) 111 log.Debugf("Not adding incoming key with kid=%s because it is configured already", kid) 112 } else { 113 result[kid] = key 114 keysCM.Data[kid] = key.KeyData 115 log.Debugf("Adding incoming key with kid=%s to database", kid) 116 } 117 } 118 119 err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data) 120 if err != nil { 121 return nil, nil, err 122 } 123 124 return result, skipped, nil 125 } 126 127 // DeleteGPGPublicKey deletes a GPG public key from the configuration 128 func (db *db) DeleteGPGPublicKey(ctx context.Context, keyID string) error { 129 keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName) 130 if err != nil { 131 return err 132 } 133 134 if keysCM.Data == nil { 135 return fmt.Errorf("No such key configured: %s", keyID) 136 } 137 138 if _, ok := keysCM.Data[keyID]; !ok { 139 return fmt.Errorf("No such key configured: %s", keyID) 140 } 141 142 delete(keysCM.Data, keyID) 143 144 err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data) 145 return err 146 }