github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/creds/ssm/manager.go (about)

     1  package ssm
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/lager"
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/aws/credentials"
    12  	"github.com/aws/aws-sdk-go/aws/session"
    13  	"github.com/aws/aws-sdk-go/service/ssm"
    14  	"github.com/pf-qiu/concourse/v6/atc/creds"
    15  )
    16  
    17  const DefaultPipelineSecretTemplate = "/concourse/{{.Team}}/{{.Pipeline}}/{{.Secret}}"
    18  const DefaultTeamSecretTemplate = "/concourse/{{.Team}}/{{.Secret}}"
    19  
    20  type SsmManager struct {
    21  	AwsAccessKeyID         string `mapstructure:"access_key" long:"access-key" description:"AWS Access key ID"`
    22  	AwsSecretAccessKey     string `mapstructure:"secret_key" long:"secret-key" description:"AWS Secret Access Key"`
    23  	AwsSessionToken        string `mapstructure:"session_token" long:"session-token" description:"AWS Session Token"`
    24  	AwsRegion              string `mapstructure:"region" long:"region" description:"AWS region to send requests to"`
    25  	PipelineSecretTemplate string `mapstructure:"pipeline_secret_template" long:"pipeline-secret-template" description:"AWS SSM parameter name template used for pipeline specific parameter" default:"/concourse/{{.Team}}/{{.Pipeline}}/{{.Secret}}"`
    26  	TeamSecretTemplate     string `mapstructure:"team_secret_template" long:"team-secret-template" description:"AWS SSM parameter name template used for team specific parameter" default:"/concourse/{{.Team}}/{{.Secret}}"`
    27  	Ssm                    *Ssm
    28  }
    29  
    30  func (manager *SsmManager) MarshalJSON() ([]byte, error) {
    31  	health, err := manager.Health()
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  
    36  	return json.Marshal(&map[string]interface{}{
    37  		"aws_region":               manager.AwsRegion,
    38  		"pipeline_secret_template": manager.PipelineSecretTemplate,
    39  		"team_secret_template":     manager.TeamSecretTemplate,
    40  		"health":                   health,
    41  	})
    42  }
    43  
    44  func (manager *SsmManager) Init(log lager.Logger) error {
    45  	session, err := manager.getSession()
    46  	if err != nil {
    47  		log.Error("failed-to-create-aws-session", err)
    48  		return err
    49  	}
    50  
    51  	manager.Ssm = &Ssm{
    52  		api: ssm.New(session),
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  func (manager *SsmManager) getSession() (*session.Session, error) {
    59  
    60  	config := &aws.Config{Region: &manager.AwsRegion}
    61  	if manager.AwsAccessKeyID != "" {
    62  		config.Credentials = credentials.NewStaticCredentials(manager.AwsAccessKeyID, manager.AwsSecretAccessKey, manager.AwsSessionToken)
    63  	}
    64  
    65  	return session.NewSession(config)
    66  }
    67  
    68  func (manager *SsmManager) Health() (*creds.HealthResponse, error) {
    69  	health := &creds.HealthResponse{
    70  		Method: "GetParameter",
    71  	}
    72  
    73  	_, _, _, err := manager.Ssm.getParameterByName("__concourse-health-check")
    74  	if err != nil {
    75  		if errObj, ok := err.(awserr.Error); ok && strings.Contains(errObj.Code(), "AccessDenied") {
    76  			health.Response = map[string]string{
    77  				"status": "UP",
    78  			}
    79  
    80  			return health, nil
    81  		}
    82  
    83  		health.Error = err.Error()
    84  		return health, nil
    85  	}
    86  
    87  	health.Response = map[string]string{
    88  		"status": "UP",
    89  	}
    90  
    91  	return health, nil
    92  }
    93  
    94  func (manager *SsmManager) IsConfigured() bool {
    95  	return manager.AwsRegion != ""
    96  }
    97  
    98  func (manager *SsmManager) Validate() error {
    99  	if _, err := creds.BuildSecretTemplate("pipeline-secret-template", manager.PipelineSecretTemplate); err != nil {
   100  		return err
   101  	}
   102  
   103  	if _, err := creds.BuildSecretTemplate("team-secret-template", manager.TeamSecretTemplate); err != nil {
   104  		return err
   105  	}
   106  
   107  	// All of the AWS credential variables may be empty since credentials may be obtained via environemnt variables
   108  	// or other means. However, if one of them is provided, then all of them (except session token) must be provided.
   109  	if manager.AwsAccessKeyID == "" && manager.AwsSecretAccessKey == "" && manager.AwsSessionToken == "" {
   110  		return nil
   111  	}
   112  
   113  	if manager.AwsAccessKeyID == "" {
   114  		return errors.New("must provide aws access key id")
   115  	}
   116  
   117  	if manager.AwsSecretAccessKey == "" {
   118  		return errors.New("must provide aws secret access key")
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  func (manager *SsmManager) NewSecretsFactory(log lager.Logger) (creds.SecretsFactory, error) {
   125  
   126  	session, err := manager.getSession()
   127  	if err != nil {
   128  		log.Error("failed-to-create-aws-session", err)
   129  		return nil, err
   130  	}
   131  
   132  	pipelineSecretTemplate, err := creds.BuildSecretTemplate("pipeline-secret-template", manager.PipelineSecretTemplate)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	teamSecretTemplate, err := creds.BuildSecretTemplate("team-secret-template", manager.TeamSecretTemplate)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	return NewSsmFactory(log, session, []*creds.SecretTemplate{pipelineSecretTemplate, teamSecretTemplate}), nil
   143  }
   144  
   145  func (manager *SsmManager) Close(logger lager.Logger) {
   146  	// TODO - to implement
   147  }