AWS Developer Tools Blog

Using Go 1.8’s Plugin for Credentials with the AWS SDK for Go

The v1.10.0 release of the AWS SDK for Go adds a new way to configure the SDK to retrieve AWS credentials. With this release, you can configure the SDK to retrieve AWS credentials from a Go plugin that is dynamically loaded during your application’s runtime. In this post, we explain how you can build a plugin and configure the SDK to use it. The SDK also includes a runnable example for you to try out the new plugin credential provider feature.

The SDK does takes advantage of the Go 1.8 plugin package, and associated build mode for Linux operating systems. The plugin package and associated build mode enable you to write components that can be loaded dynamically while your application runs. Plugins help you add functionality to your application while it’s running instead of only when the application is compiled.

The SDK’s plugincreds package enables you to use the plugins to retrieve AWS credentials. This package includes utilities to create a credentials Provider and Credentials loader.

Building a credential provider plugin

To use a plugin with the SDK, the SDK requires the plugin to export a function that returns two function pointers. The SDK uses these two returned function pointers to retrieve credentials and to determine if the credentials are expired. By default, the SDK expects the plugin to export the symbol named GetAWSSDKCredentialProvider for the getter function that returns the retrieve and isExpired function pointers.

The SDK requires the plugin’s getter function signature to match the following signature. If the getter function doesn’t match the signature, the SDK returns an error with the code ErrCodeInvalidSymbolError.

func() (RetrieveFn func() (key, secret, token string, err error), IsExpiredFn func() bool)

The SDK includes the NewCredentials helper function that looks up and validates the symbol, creating the SDK’s Credentials value automatically. You can use the returned Credentials value to configure a session or service client.

To use a custom symbol name, use the GetPluginProviderFnsByName function to look up the getter function from the plugin by name. This verifies that the symbol matches the expected signature. It also gets the credential provider’s retrieve and isExpired function pointers by calling the getter function. The retrieve and isExpired function pointers are returned. The SDK requires both function pointers to be valid and not nil.

Here is an example of a plugin that provides credential retrieve and expired functions to the application that loaded the plugin.

package main

func main() {}

// Build: go build -o plugin.so -buildmode=plugin plugin.go
func init() {
	// Initialize a mock credential provider with mock values. In a real-world usage
	// the provider's Retrieve method could reach out to the source of credentials
	// and return the credentials there, instead of this mock credential provider that statically
	// sets the credential values.
	myCredProvider = provider{"key","secret","token"}
}

// GetAWSSDKCredentialProvider is the symbol the SDK will look up and use to
// get the credential provider's retrieve and isExpired functions.
func GetAWSSDKCredentialProvider() (func() (key, secret, token string, err error), func() bool) {
	return myCredProvider.Retrieve,	myCredProvider.IsExpired
}

// Mock implementation of a type that retrieves credentials and
// returns if they are expired.
type provider struct {
	key, secret, token string
}

// Return the credentials that were previously set into the provider value.
func (p provider) Retrieve() (key, secret, token string, err error) {
	return p.key, p.secret, p.token, nil
}

func (p *provider) IsExpired() bool {
	return false;
}

Once you’ve written the code for your plugin, you can build it as a plugin file that can be loaded dynamically into your application with the -buildmode=plugin build flag.

go build -o myCredPlugin.so -buildmode=plugin plugin.go

You can find an example you can start from in the SDK’s plugincreds example.

Using a credential provider plugin

Once you’ve built your plugin, you can configure the SDK to retrieve credentials using it. The SDK makes this easy with the plugincreds package’s NewCredential function. This function takes a Plugin pointer value and looks up the expected credentials provider getter functions. See the plugincreds package for errors that can be returned.

The following example shows you how an application can open a Go plugin dynamically at runtime, and configure the SDK to use the plugin to retrieve AWS credentials.

// In your application code, open the plugin using its file name. This loads
// the plugin into memory, executing the plugin's main package init function.
p, err := plugin.Open(pluginFilename)
if err != nil {
	return nil, errors.Wrapf(err, "failed to open plugin, %s", pluginFilename)
}

// NewCredentials looks up the symbol from the plugin and configures the Credentials
// value that can be used to configure a session or service client.
//
// You can share the Credentials value and credentials, across many session and service clients 
// concurrently and safely.
creds, err := plugincreds.NewCredentials(p)
if err != nil {
	return nil, errors.Wrapf(err, "failed to load plugin credentials provider, %s", pluginFilename)
}

// Configure a session to use the credentials sourced from the plugin that is loaded.
sess := session.Must(session.NewSession(&aws.Config{
	Credentials: creds,
}))

// Return the configured session so it can be used to create service clients.
return sess, nil

You can find a usable example of this in the SDK’s plugincreds example.

Putting it all together

With this configuration, you can deploy your plugin and application independently to the platforms that your application will run on. Loading plugins dynamically allows you to separate your application from where your AWS credentials are retrieved. This practice allows your application to be more flexible when working with multiple environments. This technique is particularly useful for CLI applications where users of the CLI need to provide custom ways of retrieving credentials.

Let us know how you use the credentials plugin in your applications.