1// +build go1.8 2 3// Package plugincreds implements a credentials provider sourced from a Go 4// plugin. This package allows you to use a Go plugin to retrieve AWS credentials 5// for the SDK to use for service API calls. 6// 7// As of Go 1.8 plugins are only supported on the Linux platform. 8// 9// Plugin Symbol Name 10// 11// The "GetAWSSDKCredentialProvider" is the symbol name that will be used to 12// lookup the credentials provider getter from the plugin. If you want to use a 13// custom symbol name you should use GetPluginProviderFnsByName to lookup the 14// symbol by a custom name. 15// 16// This symbol is a function that returns two additional functions. One to 17// retrieve the credentials, and another to determine if the credentials have 18// expired. 19// 20// Plugin Symbol Signature 21// 22// The plugin credential provider requires the symbol to match the 23// following signature. 24// 25// func() (RetrieveFn func() (key, secret, token string, err error), IsExpiredFn func() bool) 26// 27// Plugin Implementation Example 28// 29// The following is an example implementation of a SDK credential provider using 30// the plugin provider in this package. See the SDK's example/aws/credential/plugincreds/plugin 31// folder for a runnable example of this. 32// 33// package main 34// 35// func main() {} 36// 37// var myCredProvider provider 38// 39// // Build: go build -o plugin.so -buildmode=plugin plugin.go 40// func init() { 41// // Initialize a mock credential provider with stubs 42// myCredProvider = provider{"a","b","c"} 43// } 44// 45// // GetAWSSDKCredentialProvider is the symbol SDK will lookup and use to 46// // get the credential provider's retrieve and isExpired functions. 47// func GetAWSSDKCredentialProvider() (func() (key, secret, token string, err error), func() bool) { 48// return myCredProvider.Retrieve, myCredProvider.IsExpired 49// } 50// 51// // mock implementation of a type that returns retrieves credentials and 52// // returns if they have expired. 53// type provider struct { 54// key, secret, token string 55// } 56// 57// func (p provider) Retrieve() (key, secret, token string, err error) { 58// return p.key, p.secret, p.token, nil 59// } 60// 61// func (p *provider) IsExpired() bool { 62// return false; 63// } 64// 65// Configuring SDK for Plugin Credentials 66// 67// To configure the SDK to use a plugin's credential provider you'll need to first 68// open the plugin file using the plugin standard library package. Once you have 69// a handle to the plugin you can use the NewCredentials function of this package 70// to create a new credentials.Credentials value that can be set as the 71// credentials loader of a Session or Config. See the SDK's example/aws/credential/plugincreds 72// folder for a runnable example of this. 73// 74// // Open plugin, and load it into the process. 75// p, err := plugin.Open("somefile.so") 76// if err != nil { 77// return nil, err 78// } 79// 80// // Create a new Credentials value which will source the provider's Retrieve 81// // and IsExpired functions from the plugin. 82// creds, err := plugincreds.NewCredentials(p) 83// if err != nil { 84// return nil, err 85// } 86// 87// // Example to configure a Session with the newly created credentials that 88// // will be sourced using the plugin's functionality. 89// sess := session.Must(session.NewSession(&aws.Config{ 90// Credentials: creds, 91// })) 92package plugincreds 93 94import ( 95 "fmt" 96 "plugin" 97 98 "github.com/aws/aws-sdk-go/aws/awserr" 99 "github.com/aws/aws-sdk-go/aws/credentials" 100) 101 102// ProviderSymbolName the symbol name the SDK will use to lookup the plugin 103// provider value from. 104const ProviderSymbolName = `GetAWSSDKCredentialProvider` 105 106// ProviderName is the name this credentials provider will label any returned 107// credentials Value with. 108const ProviderName = `PluginCredentialsProvider` 109 110const ( 111 // ErrCodeLookupSymbolError failed to lookup symbol 112 ErrCodeLookupSymbolError = "LookupSymbolError" 113 114 // ErrCodeInvalidSymbolError symbol invalid 115 ErrCodeInvalidSymbolError = "InvalidSymbolError" 116 117 // ErrCodePluginRetrieveNil Retrieve function was nil 118 ErrCodePluginRetrieveNil = "PluginRetrieveNilError" 119 120 // ErrCodePluginIsExpiredNil IsExpired Function was nil 121 ErrCodePluginIsExpiredNil = "PluginIsExpiredNilError" 122 123 // ErrCodePluginProviderRetrieve plugin provider's retrieve returned error 124 ErrCodePluginProviderRetrieve = "PluginProviderRetrieveError" 125) 126 127// Provider is the credentials provider that will use the plugin provided 128// Retrieve and IsExpired functions to retrieve credentials. 129type Provider struct { 130 RetrieveFn func() (key, secret, token string, err error) 131 IsExpiredFn func() bool 132} 133 134// NewCredentials returns a new Credentials loader using the plugin provider. 135// If the symbol isn't found or is invalid in the plugin an error will be 136// returned. 137func NewCredentials(p *plugin.Plugin) (*credentials.Credentials, error) { 138 retrieve, isExpired, err := GetPluginProviderFns(p) 139 if err != nil { 140 return nil, err 141 } 142 143 return credentials.NewCredentials(Provider{ 144 RetrieveFn: retrieve, 145 IsExpiredFn: isExpired, 146 }), nil 147} 148 149// Retrieve will return the credentials Value if they were successfully retrieved 150// from the underlying plugin provider. An error will be returned otherwise. 151func (p Provider) Retrieve() (credentials.Value, error) { 152 creds := credentials.Value{ 153 ProviderName: ProviderName, 154 } 155 156 k, s, t, err := p.RetrieveFn() 157 if err != nil { 158 return creds, awserr.New(ErrCodePluginProviderRetrieve, 159 "failed to retrieve credentials with plugin provider", err) 160 } 161 162 creds.AccessKeyID = k 163 creds.SecretAccessKey = s 164 creds.SessionToken = t 165 166 return creds, nil 167} 168 169// IsExpired will return the expired state of the underlying plugin provider. 170func (p Provider) IsExpired() bool { 171 return p.IsExpiredFn() 172} 173 174// GetPluginProviderFns returns the plugin's Retrieve and IsExpired functions 175// returned by the plugin's credential provider getter. 176// 177// Uses ProviderSymbolName as the symbol name when lookup up the symbol. If you 178// want to use a different symbol name, use GetPluginProviderFnsByName. 179func GetPluginProviderFns(p *plugin.Plugin) (func() (key, secret, token string, err error), func() bool, error) { 180 return GetPluginProviderFnsByName(p, ProviderSymbolName) 181} 182 183// GetPluginProviderFnsByName returns the plugin's Retrieve and IsExpired functions 184// returned by the plugin's credential provider getter. 185// 186// Same as GetPluginProviderFns, but takes a custom symbolName to lookup with. 187func GetPluginProviderFnsByName(p *plugin.Plugin, symbolName string) (func() (key, secret, token string, err error), func() bool, error) { 188 sym, err := p.Lookup(symbolName) 189 if err != nil { 190 return nil, nil, awserr.New(ErrCodeLookupSymbolError, 191 fmt.Sprintf("failed to lookup %s plugin provider symbol", symbolName), err) 192 } 193 194 fn, ok := sym.(func() (func() (key, secret, token string, err error), func() bool)) 195 if !ok { 196 return nil, nil, awserr.New(ErrCodeInvalidSymbolError, 197 fmt.Sprintf("symbol %T, does not match the 'func() (func() (key, secret, token string, err error), func() bool)' type", sym), nil) 198 } 199 200 retrieveFn, isExpiredFn := fn() 201 if retrieveFn == nil { 202 return nil, nil, awserr.New(ErrCodePluginRetrieveNil, 203 "the plugin provider retrieve function cannot be nil", nil) 204 } 205 if isExpiredFn == nil { 206 return nil, nil, awserr.New(ErrCodePluginIsExpiredNil, 207 "the plugin provider isExpired function cannot be nil", nil) 208 } 209 210 return retrieveFn, isExpiredFn, nil 211} 212