[node.sh] added kmsProvider.go to provide kms client

pull/3219/head
Jacky Wang 4 years ago
parent 7842d0ea34
commit 3d9ef03a33
No known key found for this signature in database
GPG Key ID: 1085CE5F4FF5842C
  1. 216
      cmd/harmony/blsloader/kmsProvider.go
  2. 6
      cmd/harmony/blsloader/params.go
  3. 0
      cmd/harmony/blsloader/passProvider.go

@ -0,0 +1,216 @@
package blsloader
import (
"encoding/json"
"fmt"
"io/ioutil"
"sync"
"syscall"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/terminal"
)
type AwsConfig struct {
AccessKey string `json:"aws-access-key-id"`
SecretKey string `json:"aws-secret-access-key"`
Region string `json:"aws-region"`
Token string `json:"aws-token,omitempty"`
}
func (cfg *AwsConfig) toAws() *aws.Config {
cred := credentials.NewStaticCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token)
return &aws.Config{
Region: aws.String(cfg.Region),
Credentials: cred,
}
}
// kmsClientProvider provides the kms client. Implemented by
// baseKMSProvider - abstract implementation
// sharedKMSProvider - provide the client with default .aws folder
// fileKMSProvider - provide the aws config with a json file
// promptKMSProvider - provide the config field from prompt with time out
type kmsClientProvider interface {
// getKMSClient returns the KMSClient of the kmsClientProvider with lazy loading.
getKMSClient() (*kms.KMS, error)
// getAWSConfig returns the AwsConfig for different implementations
getAWSConfig() (*AwsConfig, error)
}
// baseKMSProvider provide the kms client with singleton initialization through
// function getConfig for aws credential and regions loading.
type baseKMSProvider struct {
client *kms.KMS
err error
once sync.Once
}
func (provider *baseKMSProvider) getKMSClient() (*kms.KMS, error) {
provider.once.Do(func() {
cfg, err := provider.getAWSConfig()
if err != nil {
provider.err = err
return
}
provider.client, provider.err = kmsClientWithConfig(cfg)
})
if provider.err != nil {
return nil, provider.err
}
return provider.client, nil
}
func (provider *baseKMSProvider) getAWSConfig() (*AwsConfig, error) {
return nil, errors.New("not implemented")
}
// sharedKMSProvider provide the kms session with the default aws config
// locates in directory $HOME/.aws/config
type sharedKMSProvider struct {
baseKMSProvider
}
func newSharedKMSProvider() *sharedKMSProvider {
return &sharedKMSProvider{
baseKMSProvider{},
}
}
func (provider *sharedKMSProvider) getAWSConfig() (*AwsConfig, error) {
return nil, nil
}
// fileKMSProvider provide the kms session from a file with json data of structure
// AwsConfig
type fileKMSProvider struct {
baseKMSProvider
file string
}
func newFileKMSProvider(file string) *fileKMSProvider {
return &fileKMSProvider{
baseKMSProvider: baseKMSProvider{},
file: file,
}
}
func (provider *fileKMSProvider) getAWSConfig() (*AwsConfig, error) {
b, err := ioutil.ReadFile(provider.file)
if err != nil {
return nil, err
}
var cfg *AwsConfig
if err := json.Unmarshal(b, cfg); err != nil {
return nil, err
}
return cfg, nil
}
// promptKMSProvider provide a user interactive console for AWS config.
// Three fields are asked:
// 1. AccessKey 2. SecretKey 3. Region
// Each field is asked with a timeout mechanism.
type promptKMSProvider struct {
baseKMSProvider
timeout time.Duration
}
func newPromptKMSProvider(timeout time.Duration) *promptKMSProvider {
if timeout == 0 {
timeout = defPromptTimeout
}
return &promptKMSProvider{
baseKMSProvider: baseKMSProvider{},
timeout: timeout,
}
}
func (provider *promptKMSProvider) getAWSConfig() (*AwsConfig, error) {
fmt.Println("Please provide AWS configurations for KMS encoded BLS keys:")
accessKey, err := provider.prompt(" AccessKey:")
if err != nil {
return nil, fmt.Errorf("cannot get aws access key: %v", err)
}
secretKey, err := provider.prompt(" SecretKey:")
if err != nil {
return nil, fmt.Errorf("cannot get aws secret key: %v", err)
}
region, err := provider.prompt("Region:")
if err != nil {
return nil, fmt.Errorf("cannot get aws region: %v", err)
}
return &AwsConfig{
AccessKey: accessKey,
SecretKey: secretKey,
Region: region,
Token: "",
}, nil
}
// prompt prompt the user to input a string for a certain field with timeout.
func (provider *promptKMSProvider) prompt(hint string) (string, error) {
var (
res string
err error
finished = make(chan struct{})
timedOut = time.After(provider.timeout)
)
go func() {
res, err = provider.threadedPrompt(hint)
close(finished)
}()
for {
select {
case <-finished:
return res, err
case <-timedOut:
return "", errors.New("timed out")
}
}
}
func (provider *promptKMSProvider) threadedPrompt(hint string) (string, error) {
fmt.Print(hint)
b, err := terminal.ReadPassword(syscall.Stdin)
if err != nil {
return "", err
}
return string(b), nil
}
func kmsClientWithConfig(config *AwsConfig) (*kms.KMS, error) {
if config == nil {
return getSharedKMSClient()
}
return getKMSClientFromConfig(config)
}
func getSharedKMSClient() (*kms.KMS, error) {
sess, err := session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
})
if err != nil {
return nil, errors.Wrapf(err, "failed to create aws session")
}
return kms.New(sess), err
}
func getKMSClientFromConfig(config *AwsConfig) (*kms.KMS, error) {
sess, err := session.NewSession(config.toAws())
if err != nil {
return nil, err
}
return kms.New(sess), nil
}

@ -1,7 +1,13 @@
package blsloader
import "time"
const (
passExt = ".pass"
basicKeyExt = ".key"
kmsKeyExt = ".bls"
)
const (
defPromptTimeout = 1 * time.Second
)
Loading…
Cancel
Save