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  }