Skip to content

Commit

Permalink
authorized keys configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
e-asphyx committed Aug 28, 2020
1 parent a0cc34b commit 7ec0ff9
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 114 deletions.
14 changes: 6 additions & 8 deletions cmd/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,13 @@ func NewRootCommand(c *Context, name string) *cobra.Command {

log.SetLevel(lv)

pol, err := signatory.PreparePolicy(conf.Tezos)
if err != nil {
return err
}

sigConf := signatory.Config{
Policy: conf.Tezos,
Policy: pol,
Vaults: conf.Vaults,
Interceptor: metrics.Interceptor,
Watermark: signatory.NewInMemoryWatermark(),
Expand Down Expand Up @@ -76,10 +81,3 @@ func NewRootCommand(c *Context, name string) *cobra.Command {

return &rootCmd
}

/*
// Execute executes root command
func Execute(ctx context.Context) error {
return newRootCommand(ctx).Execute()
}
*/
15 changes: 14 additions & 1 deletion cmd/commands/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/ecadlabs/signatory/pkg/server"
"github.com/ecadlabs/signatory/pkg/server/auth"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
Expand All @@ -20,7 +21,19 @@ func NewServeCommand(c *Context) *cobra.Command {
Address: c.config.Server.Address,
Signer: c.signatory,
}
srv := srvConf.New()

if c.config.Server.AuthorizedKeys != nil {
ak, err := auth.StaticAuthorizedKeysFromString(c.config.Server.AuthorizedKeys.List()...)
if err != nil {
return err
}
srvConf.Auth = ak
}

srv, err := srvConf.New()
if err != nil {
return err
}
log.Printf("HTTP server is listening for connections on %s", srv.Addr)

srvErrCh := make(chan error)
Expand Down
10 changes: 10 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ server:
address: :6732
# Address for the utility HTTP server to listen on
utility_address: :9583
# List of authorized public keys. Sign operation must be authenticated if present
# Nested lists are allowed
authorized_keys:
- &authorized_key edpktpQKJF4vRodmSfT3h6LrYisshQuJeoybUxB9c8s3b1QymvisHC
- &authorized_keys_list
- edpkubVAm7SttSV2WigeSoB7fAnr612PJo9DnhCgQyXakjaMeweN1D
- &picked_key edpkuNVUYqMD61DyQ7N378nGCjCnQ3h2BiEsfeeY64kwKVJAJ4C9sY

vaults:
# Name is used to identify backend during import process
Expand Down Expand Up @@ -61,6 +68,9 @@ tezos:
# List of [endorsement, ballot, reveal, transaction, origination, delegation, seed_nonce_revelation, activate_account]
- transaction
- endorsement
authorized_keys:
# Allow sign operation only for clients specified below. Same syntax as `server/authorized_key`
- *authorized_key
```

## Backends
Expand Down
5 changes: 1 addition & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ require (
github.com/certusone/yubihsm-go v0.1.1-0.20190828101841-d0ca2ed0df7b
github.com/decred/dcrd/dcrec/secp256k1 v1.0.3
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.3.0
github.com/google/go-cmp v0.5.2 // indirect
github.com/google/tink/go v1.4.0
github.com/gorilla/mux v1.8.0
github.com/leodido/go-urn v1.2.0 // indirect
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/common v0.13.0 // indirect
github.com/segmentio/ksuid v1.0.3
Expand All @@ -27,7 +26,5 @@ require (
google.golang.org/api v0.30.0
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987
google.golang.org/grpc v1.31.1 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,14 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
Expand Down Expand Up @@ -779,10 +783,6 @@ gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
Expand Down
65 changes: 52 additions & 13 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
package config

import (
"encoding/json"
"errors"
"io/ioutil"
"net"

"gopkg.in/go-playground/validator.v9"
"github.com/go-playground/validator/v10"
yaml "gopkg.in/yaml.v3"
)

// ServerConfig contains the information necessary to the tezos signing server
type ServerConfig struct {
Address string `yaml:"address" validate:"required,hostport"`
UtilityAddress string `yaml:"utility_address" validate:"required,hostport"`
Address string `yaml:"address" validate:"required,hostname_port"`
UtilityAddress string `yaml:"utility_address" validate:"required,hostname_port"`
AuthorizedKeys *AuthorizedKeys `yaml:"authorized_keys"`
}

// TezosConfig contains the configuration related to tezos network
type TezosConfig map[string]*TezosPolicy

// TezosPolicy contains policy definition for a specific address
type TezosPolicy struct {
AllowedOperations []string `yaml:"allowed_operations" validate:"dive,oneof=generic block endorsement"`
AllowedKinds []string `yaml:"allowed_kinds" validate:"dive,oneof=endorsement seed_nonce_revelation activate_account ballot reveal transaction origination delegation"`
LogPayloads bool `yaml:"log_payloads"`
AllowedOperations []string `yaml:"allowed_operations" validate:"dive,oneof=generic block endorsement"`
AllowedKinds []string `yaml:"allowed_kinds" validate:"dive,oneof=endorsement seed_nonce_revelation activate_account ballot reveal transaction origination delegation"`
LogPayloads bool `yaml:"log_payloads"`
AuthorizedKeys *AuthorizedKeys `yaml:"authorized_keys"`
}

// VaultConfig represents single vault instance
Expand Down Expand Up @@ -50,11 +53,47 @@ func (c *Config) Read(file string) error {
return nil
}

// Validator returns new validator instance
func Validator() *validator.Validate {
validate := validator.New()
validate.RegisterValidation("hostport", func(fl validator.FieldLevel) bool {
_, _, err := net.SplitHostPort(fl.Field().String())
return err == nil
})
return validate
return validator.New()
}

// AuthorizedKeys keeps list of authorized public keys
type AuthorizedKeys struct {
value string
list []*AuthorizedKeys
}

// List returns all keys as a string slice
func (a *AuthorizedKeys) List() []string {
if a.list != nil {
var ret []string
for _, v := range a.list {
ret = append(ret, v.List()...)
}
return ret
}
return []string{a.value}
}

// UnmarshalYAML implements yaml.Unmarshaler
func (a *AuthorizedKeys) UnmarshalYAML(value *yaml.Node) error {
var target interface{}
switch value.Kind {
case yaml.ScalarNode:
target = &a.value
case yaml.SequenceNode:
target = &a.list
default:
return errors.New("can't decode YAML node")
}
if err := value.Decode(target); err != nil {
return err
}
return nil
}

// MarshalJSON implements json.Marshaler
func (a *AuthorizedKeys) MarshalJSON() ([]byte, error) {
return json.Marshal(a.List())
}
46 changes: 26 additions & 20 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const defaultAddr = ":6732"

// Signer interface representing a Signer (currently implemented by Signatory)
type Signer interface {
Sign(ctx context.Context, keyHash string, message []byte) (string, error)
Sign(ctx context.Context, req *signatory.SignRequest) (string, error)
GetPublicKey(ctx context.Context, keyHash string) (*signatory.PublicKey, error)
}

Expand All @@ -40,26 +40,26 @@ func (s *Server) logger() log.FieldLogger {
return log.StandardLogger()
}

func signRequestToSign(payload []byte, keyHash string) ([]byte, error) {
keyHashBytes, err := tezos.EncodeBinaryPublicKeyHash(keyHash)
func signRequestToSign(req *signatory.SignRequest) ([]byte, error) {
keyHashBytes, err := tezos.EncodeBinaryPublicKeyHash(req.PublicKeyHash)
if err != nil {
return nil, err
}
data := make([]byte, 2+len(payload)+len(keyHashBytes))
data := make([]byte, 2+len(req.Message)+len(keyHashBytes))
data[0] = 4
data[1] = 1
copy(data[2:], keyHashBytes)
copy(data[2+len(keyHashBytes):], payload)
copy(data[2+len(keyHashBytes):], req.Message)
return data, nil
}

func (s *Server) authenticateSignRequest(r *http.Request, pkh string, data []byte) error {
func (s *Server) authenticateSignRequest(req *signatory.SignRequest, r *http.Request) error {
v := r.FormValue("authentication")
if v == "" {
return errors.Wrap(stderr.New("missing authentication signature field"), http.StatusUnauthorized)
}

signed, err := signRequestToSign(data, pkh)
signed, err := signRequestToSign(req)
if err != nil {
return errors.Wrap(err, http.StatusBadRequest)
}
Expand All @@ -75,7 +75,6 @@ func (s *Server) authenticateSignRequest(r *http.Request, pkh string, data []byt
return err
}

ok := false
for _, pkh := range hashes {
pub, err := s.Auth.GetPublicKey(r.Context(), pkh)
if err != nil {
Expand All @@ -84,21 +83,20 @@ func (s *Server) authenticateSignRequest(r *http.Request, pkh string, data []byt

err = cryptoutils.Verify(pub, digest[:], sig)
if err == nil {
ok = true
break
req.ClientPublicKeyHash = pkh
return nil
} else if err != cryptoutils.ErrSignature {
return err
}
}

if !ok {
return errors.Wrap(stderr.New("invalid authentication signature"), http.StatusForbidden)
}
return nil
return errors.Wrap(stderr.New("invalid authentication signature"), http.StatusForbidden)
}

func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) {
keyHash := mux.Vars(r)["key"]
signRequest := signatory.SignRequest{
PublicKeyHash: mux.Vars(r)["key"],
}

defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
Expand All @@ -114,21 +112,21 @@ func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) {
return
}

data, err := hex.DecodeString(req)
signRequest.Message, err = hex.DecodeString(req)
if err != nil {
tezosJSONError(w, errors.Wrap(err, http.StatusBadRequest))
return
}

if s.Auth != nil {
if err = s.authenticateSignRequest(r, keyHash, data); err != nil {
if err = s.authenticateSignRequest(&signRequest, r); err != nil {
s.logger().Error(err)
tezosJSONError(w, err)
return
}
}

signature, err := s.Signer.Sign(r.Context(), keyHash, data)
signature, err := s.Signer.Sign(r.Context(), &signRequest)
if err != nil {
s.logger().Errorf("Error signing request: %v", err)
tezosJSONError(w, err)
Expand Down Expand Up @@ -178,7 +176,15 @@ func (s *Server) authorizedKeysHandler(w http.ResponseWriter, r *http.Request) {
}

// New returns a new http server with registered routes
func (s *Server) New() *http.Server {
func (s *Server) New() (*http.Server, error) {
if s.Auth != nil {
hashes, err := s.Auth.ListPublicKeys(context.Background())
if err != nil {
return nil, err
}
s.logger().Infof("Authorized keys: %v", hashes)
}

r := mux.NewRouter()
r.Use((&Logging{}).Handler)

Expand All @@ -196,5 +202,5 @@ func (s *Server) New() *http.Server {
Addr: addr,
}

return srv
return srv, nil
}
Loading

0 comments on commit 7ec0ff9

Please sign in to comment.