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  }