# Sandstorm Go SDK

The `Manager` SDK is what interacts with KMS and DynamoDB and can be used as an in-memory solution for loading secrets into your application. It's instantiated by passing a `Config` object to `manager.New()`.

## Full Documentation

[Here](https://godoc.internal.justin.tv/code.justin.tv/systems/sandstorm/manager).

## Configuration

The `Config` object has fields for setting AWS configuration. By
default you can leave `AWSConfig` nil and it will fill in with a
default config using `us-west-2`. `TableName` is the name of the main
DynamoDB table. `KeyID` is the KMS master key to use, either a key
alias (prefixed with `alias/`), a key ARN or a key UUID.

For Sandstorm production table/KMS Key, please use the TableName and KeyID below.


```go
config := manager.Config{
	AWSConfig: &aws.Config{Region: aws.String("us-west-2")},
	TableName: "sandstorm-production",
	KeyID: "alias/sandstorm-production'	
}
	
m := manager.New(config)
```

## Secret object
The public fields of the Secret object are:

```go
type Secret struct {
	// Name of secret
	Name string `json:"name"`
	// Plaintext to be enciphered or read by user
	Plaintext []byte `json:"plaintext,omitempty"`
	// Unix timestamp of when the key was created/updated
	UpdatedAt int64 `json:"updated_at"`
	// Ciphertext to be deciphered or uploaded
	DoNotBroadcast bool `json:"do_not_broadcast"`
	// Tombstone signifies that a secret has been deleted
	Tombstone bool `json:"tombstone"`
	// KeyARN is the key used to encrypt
	KeyARN     string `json:"key_arn"`
}
```

`Name` and `Plaintext` are required. `UpdatedAt` is filled in by the
`Put` operation when persisting the secret. `DoNotBroadcast` is a flag
to tell the SNS notifier to not publish that this secret has been
updated. `Tombstone` is a flag marking that a secret was deleted in
the audit table.


## Manager operations relating to secrets
* `List() ([]Secret, error)` lists all secrets. This operation is
  normally not accessible to normal IAM roles since this requires
  `dynamodb:Scan` on the main table.
* `Put(secret *Secret) error` encrypts and uploads a new version of a
  secret to both the main table and the audit table.
* `Get(name string) (*Secret, error)` downloads the latest version of
  a secret. Please use the fully namespaced secret.
* `Delete(name string) error` deletes a secret from the main table and
  writes a tombstone to the audit table.

## Namespaces

Namespaces are a collection of secrets. A secret's namespace is
derived from the name of the secret leading up until the first
`/`. This enables IAM policies to allow access to all secrets in a
certain namespace.

## Manager operations relating to namespaces
* `ListNamespaces() ([]string, error)` - lists all namespaces by calling
  `dynamodb:Scan` on the namespaces table.

* `ListNamespace(namespace string) ([]Secret, error)` - lists all
  secrets in a namespace by calling `dynamodb:Query` on the
  `namespace_name` global secondary index in the main table.


## Cross-account (EC2/AWS) example

```go
package main

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws/session"
	"code.justin.tv/systems/sandstorm/manager"
	"fmt"
	"time"
)

func main() {

	// Config for us-west-2 for STS
	awsConfig := &aws.Config{Region: aws.String("us-west-2")}
	stsclient := sts.New(session.New(awsConfig))


	// Create stscreds.AssumeRoleProvider with a 15 minute duration
	// for the role <CROSS ACCOUNT ROLE FROM DASHBOARD HERE> in our main account
	arp := &stscreds.AssumeRoleProvider{
		Duration:     900 * time.Second,
		ExpiryWindow: 10 * time.Second,
		RoleARN: "<CROSS ACCOUNT ROLE FROM DASHBOARD HERE>",
		Client:       stsclient,
	}

	// Create credentials and config using our Assumed Role that we
	// will use to access the main account with
	credentials := credentials.NewCredentials(arp)
	awsConfig.WithCredentials(credentials)
	

	// Finally, create a secret manager that uses the cross-account
	// config.
	sandstormManager = manager.New(manager.Config{
		AWSConfig: awsConfig,
		TableName: "sandstorm-production",
		KeyID: "alias/sandstorm-production",
	})


	secret, err := m.Get("syseng/testerino/production/test")
	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", secret)
	fmt.Println(string(secret.Plaintext))
}
```

## Baremetal Example

```go
package main

import (
	"code.justin.tv/systems/sandstorm/manager"
	"fmt"
)

func main() {
	m := manager.New(manager.Config{TableName: "sandstorm-production",
	KeyID: "alias/sandstorm-production"})
	// Store a new secret
	secret := &manager.Secret{
		Name: "database_password",
		Plaintext: []byte("supersecret"),
	}
	err := m.Put(secret)
	if err != nil {
		panic(err)
	}

	// List secrets
	secrets, err := m.List()
	if err != nil {
		panic(err)
	}
	for _, secret := range secrets {
		fmt.Println(secret.Name)
	}

	// Fetch a secret
	newSecret, err := m.Get("syseng/testerino/production/test")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(newSecret.Plaintext))
}
```
