go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/cli/config/aws-ssm-ps.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package config
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"path"
    10  	"regexp"
    11  	"strings"
    12  
    13  	"github.com/aws/aws-sdk-go-v2/aws"
    14  	"github.com/aws/aws-sdk-go-v2/config"
    15  	"github.com/aws/aws-sdk-go-v2/service/ssm"
    16  	"github.com/cockroachdb/errors"
    17  	"github.com/rs/zerolog/log"
    18  	"github.com/spf13/viper"
    19  )
    20  
    21  const AWS_SSM_PARAMETERSTORE_PREFIX = "aws-ssm-ps://"
    22  
    23  // loads the configuration from aws ssm parameter store
    24  func loadAwsSSMParameterStore(key string) error {
    25  	viper.RemoteConfig = &awsSSMParamConfigFactory{}
    26  	viper.SupportedRemoteProviders = []string{"aws-ssm-ps"}
    27  	ssmKey := strings.TrimPrefix(key, AWS_SSM_PARAMETERSTORE_PREFIX)
    28  	log.Info().Str("key", ssmKey).Msg("look for configuration stored in aws ssm parameter store")
    29  	err := viper.AddRemoteProvider("aws-ssm-ps", "localhost", ssmKey)
    30  	if err != nil {
    31  		return errors.Wrap(err, "could not initialize gs provider")
    32  	}
    33  	viper.SetConfigType("yaml")
    34  	err = viper.ReadRemoteConfig()
    35  	if err != nil {
    36  		return errors.Wrapf(err, "could not read aws ssm parameter config from %s", ssmKey)
    37  	}
    38  
    39  	return nil
    40  }
    41  
    42  type awsSSMParamConfigFactory struct{}
    43  
    44  func (a *awsSSMParamConfigFactory) Get(rp viper.RemoteProvider) (io.Reader, error) {
    45  	ssmParameter, err := parseSsmParameterPath(rp.Path())
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	ctx := context.Background()
    50  
    51  	cfg, err := config.LoadDefaultConfig(ctx)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	cfg.Region = ssmParameter.Region
    57  	ps := ssm.NewFromConfig(cfg)
    58  
    59  	out, err := ps.GetParameter(ctx, &ssm.GetParameterInput{
    60  		Name:           aws.String(ssmParameter.Parameter),
    61  		WithDecryption: aws.Bool(true), // this field is ignored if the parameter is a string or stringlist, so it's ok to have it on by default
    62  	})
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return strings.NewReader(*out.Parameter.Value), nil
    68  }
    69  
    70  func (g *awsSSMParamConfigFactory) Watch(rp viper.RemoteProvider) (io.Reader, error) {
    71  	return nil, errors.New("not implemented")
    72  }
    73  
    74  func (g *awsSSMParamConfigFactory) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
    75  	return nil, nil
    76  }
    77  
    78  type SsmParameter struct {
    79  	Parameter string
    80  	Region    string
    81  	// todo: add optional decrypt and account arguments
    82  }
    83  
    84  func newSsmParameter(region string, parameter string) (*SsmParameter, error) {
    85  	if region == "" || parameter == "" {
    86  		return nil, errors.New("invalid parameter. region and parameter name required.")
    87  	}
    88  	return &SsmParameter{Region: region, Parameter: parameter}, nil
    89  }
    90  
    91  func (s *SsmParameter) String() string {
    92  	// e.g. region/us-east-2/parameter/MondooAgentConfig
    93  	return path.Join("region", s.Region, "parameter", s.Parameter)
    94  }
    95  
    96  func parseSsmParameterPath(path string) (*SsmParameter, error) {
    97  	if !isValidSSMParameterPath(path) {
    98  		return nil, errors.New("invalid parameter path. expected region/<region-val>/parameter/<parameter-name>")
    99  	}
   100  	keyValues := strings.Split(path, "/")
   101  	if len(keyValues) != 4 {
   102  		return nil, errors.New("invalid parameter path. expected region/<region-val>/parameter/<parameter-name>")
   103  	}
   104  	return newSsmParameter(keyValues[1], keyValues[3])
   105  }
   106  
   107  var VALID_SSM_PARAMETER_PATH = regexp.MustCompile(`^region\/(us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d\/parameter\/.+$`)
   108  
   109  func isValidSSMParameterPath(path string) bool {
   110  	return VALID_SSM_PARAMETER_PATH.MatchString(path)
   111  }