github.com/argoproj/argo-cd/v3@v3.2.1/util/db/gpgkeys.go (about) 1 package db 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 9 log "github.com/sirupsen/logrus" 10 11 "github.com/argoproj/argo-cd/v3/common" 12 appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 13 "github.com/argoproj/argo-cd/v3/util/gpg" 14 ) 15 16 // Validates a single GnuPG key and returns the key's ID 17 func validatePGPKey(keyData string) (*appsv1.GnuPGPublicKey, error) { 18 f, err := os.CreateTemp("", "gpg-public-key") 19 if err != nil { 20 return nil, err 21 } 22 defer os.Remove(f.Name()) 23 24 err = os.WriteFile(f.Name(), []byte(keyData), 0o600) 25 if err != nil { 26 return nil, err 27 } 28 defer func() { 29 err = f.Close() 30 if err != nil { 31 log.Errorf("error closing file %q: %v", f.Name(), err) 32 } 33 }() 34 35 parsed, err := gpg.ValidatePGPKeys(f.Name()) 36 if err != nil { 37 return nil, err 38 } 39 40 // Each key/value pair in the config map must exactly contain one public key, with the (short) GPG key ID as key 41 if len(parsed) != 1 { 42 return nil, errors.New("more than one key found in input data") 43 } 44 45 var retKey *appsv1.GnuPGPublicKey 46 // Is there a better way to get the first element from a map without knowing its key? 47 for _, k := range parsed { 48 retKey = k 49 break 50 } 51 if retKey != nil { 52 retKey.KeyData = keyData 53 return retKey, nil 54 } 55 return nil, errors.New("could not find the GPG key") 56 } 57 58 // ListConfiguredGPGPublicKeys returns a list of all configured GPG public keys from the ConfigMap 59 func (db *db) ListConfiguredGPGPublicKeys(_ 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 expectedKeyID := gpg.KeyID(k) 73 if expectedKeyID == "" { 74 return nil, fmt.Errorf("found entry with key '%s' in ConfigMap, but this is not a valid PGP key ID", k) 75 } 76 parsedKey, err := validatePGPKey(p) 77 if err != nil { 78 return nil, fmt.Errorf("could not parse GPG key for entry '%s': %s", expectedKeyID, err.Error()) 79 } 80 if expectedKeyID != parsedKey.KeyID { 81 return nil, fmt.Errorf("key parsed for entry with key ID '%s' had different key ID '%s'", expectedKeyID, parsedKey.KeyID) 82 } 83 result[parsedKey.KeyID] = parsedKey 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 for kid, key := range keys { 105 if _, ok := keysCM.Data[kid]; ok { 106 skipped = append(skipped, kid) 107 log.Debugf("Not adding incoming key with kid=%s because it is configured already", kid) 108 } else { 109 result[kid] = key 110 keysCM.Data[kid] = key.KeyData 111 log.Debugf("Adding incoming key with kid=%s to database", kid) 112 } 113 } 114 115 err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data) 116 if err != nil { 117 return nil, nil, err 118 } 119 120 return result, skipped, nil 121 } 122 123 // DeleteGPGPublicKey deletes a GPG public key from the configuration 124 func (db *db) DeleteGPGPublicKey(ctx context.Context, keyID string) error { 125 keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName) 126 if err != nil { 127 return err 128 } 129 130 if _, ok := keysCM.Data[keyID]; !ok { 131 return fmt.Errorf("no such key configured: %s", keyID) 132 } 133 134 delete(keysCM.Data, keyID) 135 136 err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data) 137 return err 138 }