Skip to content

Commit

Permalink
Introduce key service flag optionally prompting users on encryption/d…
Browse files Browse the repository at this point in the history
…ecryption (#322)
  • Loading branch information
autrilla authored Apr 11, 2018
1 parent 16950d0 commit 37b6fff
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 16 deletions.
14 changes: 12 additions & 2 deletions cmd/sops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,23 @@ func main() {
Usage: "address to listen on, e.g. '127.0.0.1:5000' or '/tmp/sops.sock'",
Value: "127.0.0.1:5000",
},
cli.BoolFlag{
Name: "prompt",
Usage: "Prompt user to confirm every incoming request",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Enable verbose logging output",
},
},
Action: func(c *cli.Context) error {
if c.Bool("verbose") {
logging.SetLevel(logrus.DebugLevel)
}
return keyservicecmd.Run(keyservicecmd.Opts{
Network: c.String("network"),
Address: c.String("address"),
Prompt: c.Bool("prompt"),
})
},
},
Expand Down Expand Up @@ -356,8 +368,6 @@ func main() {
app.Action = func(c *cli.Context) error {
if c.Bool("verbose") {
logging.SetLevel(logrus.DebugLevel)
} else {
logging.SetLevel(logrus.WarnLevel)
}
if c.NArg() < 1 {
return common.NewExitError("Error: no file specified", codes.NoFileSpecified)
Expand Down
5 changes: 4 additions & 1 deletion cmd/sops/subcommand/keyservice/keyservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func init() {
type Opts struct {
Network string
Address string
Prompt bool
}

// Run runs a SOPS key service server
Expand All @@ -33,7 +34,9 @@ func Run(opts Opts) error {
}
defer lis.Close()
grpcServer := grpc.NewServer()
keyservice.RegisterKeyServiceServer(grpcServer, keyservice.Server{})
keyservice.RegisterKeyServiceServer(grpcServer, keyservice.Server{
Prompt: opts.Prompt,
})
log.Infof("Listening on %s://%s", opts.Network, opts.Address)

// Close socket if we get killed
Expand Down
76 changes: 63 additions & 13 deletions keyservice/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keyservice

import (
"fmt"

"go.mozilla.org/sops/gcpkms"
"go.mozilla.org/sops/kms"
"go.mozilla.org/sops/pgp"
Expand All @@ -11,7 +13,10 @@ import (
)

// Server is a key service server that uses SOPS MasterKeys to fulfill requests
type Server struct{}
type Server struct {
// Prompt indicates whether the server should prompt before decrypting or encrypting data
Prompt bool
}

func (ks *Server) encryptWithPgp(key *PgpKey, plaintext []byte) ([]byte, error) {
pgpKey := pgp.NewMasterKeyFromFingerprint(key.Fingerprint)
Expand Down Expand Up @@ -86,71 +91,116 @@ func (ks *Server) decryptWithGcpKms(key *GcpKmsKey, ciphertext []byte) ([]byte,
func (ks Server) Encrypt(ctx context.Context,
req *EncryptRequest) (*EncryptResponse, error) {
key := *req.Key
var response *EncryptResponse
switch k := key.KeyType.(type) {
case *Key_PgpKey:
ciphertext, err := ks.encryptWithPgp(k.PgpKey, req.Plaintext)
if err != nil {
return nil, err
}
return &EncryptResponse{
response = &EncryptResponse{
Ciphertext: ciphertext,
}, nil
}
case *Key_KmsKey:
ciphertext, err := ks.encryptWithKms(k.KmsKey, req.Plaintext)
if err != nil {
return nil, err
}
return &EncryptResponse{
response = &EncryptResponse{
Ciphertext: ciphertext,
}, nil
}
case *Key_GcpKmsKey:
ciphertext, err := ks.encryptWithGcpKms(k.GcpKmsKey, req.Plaintext)
if err != nil {
return nil, err
}
return &EncryptResponse{
response = &EncryptResponse{
Ciphertext: ciphertext,
}, nil
}
case nil:
return nil, status.Errorf(codes.NotFound, "Must provide a key")
default:
return nil, status.Errorf(codes.NotFound, "Unknown key type")
}
if ks.Prompt {
err := ks.prompt(key, "encrypt")
if err != nil {
return nil, err
}
}
return response, nil
}

func keyToString(key Key) string {
switch k := key.KeyType.(type) {
case *Key_PgpKey:
return fmt.Sprintf("PGP key with fingerprint %s", k.PgpKey.Fingerprint)
case *Key_KmsKey:
return fmt.Sprintf("AWS KMS key with ARN %s", k.KmsKey.Arn)
case *Key_GcpKmsKey:
return fmt.Sprintf("GCP KMS key with resource ID %s", k.GcpKmsKey.ResourceId)
default:
return fmt.Sprintf("Unknown key type")
}
}

func (ks Server) prompt(key Key, requestType string) error {
keyString := keyToString(key)
var response string
for response != "y" && response != "n" {
fmt.Printf("\nReceived %s request using %s. Respond to request? (y/n): ", requestType, keyString)
_, err := fmt.Scanln(&response)
if err != nil {
return err
}
}
if response == "n" {
return grpc.Errorf(codes.PermissionDenied, "Request rejected by user")
}
return nil
}

// Decrypt takes a decrypt request and decrypts the provided ciphertext with the provided key, returning the decrypted
// result
func (ks Server) Decrypt(ctx context.Context,
req *DecryptRequest) (*DecryptResponse, error) {
key := *req.Key
var response *DecryptResponse
switch k := key.KeyType.(type) {
case *Key_PgpKey:
plaintext, err := ks.decryptWithPgp(k.PgpKey, req.Ciphertext)
if err != nil {
return nil, err
}
return &DecryptResponse{
response = &DecryptResponse{
Plaintext: plaintext,
}, nil
}
case *Key_KmsKey:
plaintext, err := ks.decryptWithKms(k.KmsKey, req.Ciphertext)
if err != nil {
return nil, err
}
return &DecryptResponse{
response = &DecryptResponse{
Plaintext: plaintext,
}, nil
}
case *Key_GcpKmsKey:
plaintext, err := ks.decryptWithGcpKms(k.GcpKmsKey, req.Ciphertext)
if err != nil {
return nil, err
}
return &DecryptResponse{
response = &DecryptResponse{
Plaintext: plaintext,
}, nil
}
case nil:
return nil, grpc.Errorf(codes.NotFound, "Must provide a key")
default:
return nil, grpc.Errorf(codes.NotFound, "Unknown key type")
}
if ks.Prompt {
err := ks.prompt(key, "decrypt")
if err != nil {
return nil, err
}
}
return response, nil
}

0 comments on commit 37b6fff

Please sign in to comment.