From 5df0f7f5ef78f97c68ac7cfcd9fac3bb9905fbe6 Mon Sep 17 00:00:00 2001 From: Eugene Zagidullin Date: Wed, 16 Oct 2024 19:40:14 +0300 Subject: [PATCH 1/4] gotez v2.1.2 fixes Paris DAL attestation issue (#506) * gotez v2.1.2 fixes Paris DAL attestation issue * mod tidy --- go.mod | 18 ++++++++++-------- go.sum | 38 ++++++++++++++------------------------ 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index af1ac739..bd2d4db5 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,15 @@ module github.com/ecadlabs/signatory -go 1.21 +go 1.22 + +toolchain go1.23.1 require ( cloud.google.com/go/kms v1.15.5 github.com/certusone/yubihsm-go v0.3.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/ecadlabs/goblst v1.0.0 - github.com/ecadlabs/gotez/v2 v2.0.6 + github.com/ecadlabs/gotez/v2 v2.1.2 github.com/go-playground/validator/v10 v10.16.0 github.com/google/tink/go v1.7.0 github.com/google/uuid v1.4.0 @@ -20,7 +22,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/oauth2 v0.15.0 google.golang.org/api v0.152.0 @@ -52,9 +54,9 @@ require ( github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - golang.org/x/sync v0.5.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 // indirect @@ -82,9 +84,9 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.24.0 // indirect golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 + golang.org/x/text v0.19.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index 9ed84589..835fea97 100644 --- a/go.sum +++ b/go.sum @@ -35,10 +35,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/ecadlabs/goblst v1.0.0 h1:8/e3SQGwqbV0+ul+pg0aSNFfC3lgQcvEed3VdDBXSl8= github.com/ecadlabs/goblst v1.0.0/go.mod h1:s67gqaOol9o6fguh+evH75X5uQniOhv1HG/EU8xPLPY= -github.com/ecadlabs/gotez/v2 v2.0.5 h1:RmpCEKYlQD18TtL9vjSDRe3FYADhxlKh80DZQz8h31Q= -github.com/ecadlabs/gotez/v2 v2.0.5/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU= -github.com/ecadlabs/gotez/v2 v2.0.6 h1:P7eQ2G+SO1tTV4NHnkdNlrOHxKDo1iF9m34HTLfS3b8= -github.com/ecadlabs/gotez/v2 v2.0.6/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU= +github.com/ecadlabs/gotez/v2 v2.1.2 h1:6VVORG5cFhHsTLbJ5X5SxmK6OnlpmVM5iEOXQNTm5Z4= +github.com/ecadlabs/gotez/v2 v2.1.2/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 h1:7WdblGykGxtGGtchW4kzTaJJO8Fm+JKhLzhttOOWr9k= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04/go.mod h1:VApUlocsLMpp4hUXHxTTIlosebnwo0BM6e1hy78qTPM= github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 h1:D22EM5TeYZJp43hGDx6dUng8mvtyYbB9BnE3+BmJR1Q= @@ -187,8 +185,8 @@ github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGy github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= @@ -222,10 +220,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= @@ -253,8 +249,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -270,26 +266,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= From a28360603dd970da5863c825a69673662eae5bae Mon Sep 17 00:00:00 2001 From: Eugene Zagidullin Date: Wed, 16 Oct 2024 23:36:24 +0300 Subject: [PATCH 2/4] Paris DAL attestation fix (#508) * gotez v2.1.2 fixes Paris DAL attestation issue * mod tidy * gotez v2.1.3 * use correct and request type names while allowing legacy ones --- go.mod | 2 +- go.sum | 4 ++-- pkg/signatory/signatory.go | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index bd2d4db5..a45c1f64 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/certusone/yubihsm-go v0.3.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/ecadlabs/goblst v1.0.0 - github.com/ecadlabs/gotez/v2 v2.1.2 + github.com/ecadlabs/gotez/v2 v2.1.3 github.com/go-playground/validator/v10 v10.16.0 github.com/google/tink/go v1.7.0 github.com/google/uuid v1.4.0 diff --git a/go.sum b/go.sum index 835fea97..154e0d01 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etly github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/ecadlabs/goblst v1.0.0 h1:8/e3SQGwqbV0+ul+pg0aSNFfC3lgQcvEed3VdDBXSl8= github.com/ecadlabs/goblst v1.0.0/go.mod h1:s67gqaOol9o6fguh+evH75X5uQniOhv1HG/EU8xPLPY= -github.com/ecadlabs/gotez/v2 v2.1.2 h1:6VVORG5cFhHsTLbJ5X5SxmK6OnlpmVM5iEOXQNTm5Z4= -github.com/ecadlabs/gotez/v2 v2.1.2/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU= +github.com/ecadlabs/gotez/v2 v2.1.3 h1:RGNtvb+UAtstTQYCsdE4XAeaEZwj3a5AliLluEOsoAg= +github.com/ecadlabs/gotez/v2 v2.1.3/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 h1:7WdblGykGxtGGtchW4kzTaJJO8Fm+JKhLzhttOOWr9k= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04/go.mod h1:VApUlocsLMpp4hUXHxTTIlosebnwo0BM6e1hy78qTPM= github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 h1:D22EM5TeYZJp43hGDx6dUng8mvtyYbB9BnE3+BmJR1Q= diff --git a/pkg/signatory/signatory.go b/pkg/signatory/signatory.go index 2d8d1175..d28c263c 100644 --- a/pkg/signatory/signatory.go +++ b/pkg/signatory/signatory.go @@ -143,7 +143,7 @@ func (s *Signatory) logger() log.FieldLogger { } var defaultPolicy = PublicKeyPolicy{ - AllowedRequests: []string{"block", "preendorsement", "endorsement"}, + AllowedRequests: []string{"block", "preattestation", "attestation"}, } func (s *Signatory) fetchPolicyOrDefault(keyHash crypt.PublicKeyHash) *PublicKeyPolicy { @@ -596,6 +596,18 @@ func (s *Signatory) Ready(ctx context.Context) (bool, error) { return true, nil } +func fixupRequests(req []string) { + for i := range req { + switch req[i] { + case "endorsement": + req[i] = "attestation" + case "preendorsement": + req[i] = "preattestation" + } + } + sort.Strings(req) +} + // PreparePolicy prepares policy data by hashing keys etc func PreparePolicy(src config.TezosConfig) (out Policy, err error) { policy := make(Policy, len(src)) @@ -614,7 +626,7 @@ func PreparePolicy(src config.TezosConfig) (out Policy, err error) { for req := range v.Allow { pol.AllowedRequests = append(pol.AllowedRequests, req) } - sort.Strings(pol.AllowedRequests) + fixupRequests(pol.AllowedRequests) if ops, ok := v.Allow["generic"]; ok { pol.AllowedOps = make([]string, len(ops)) @@ -625,7 +637,7 @@ func PreparePolicy(src config.TezosConfig) (out Policy, err error) { if v.AllowedOperations != nil { pol.AllowedRequests = make([]string, len(v.AllowedOperations)) copy(pol.AllowedRequests, v.AllowedOperations) - sort.Strings(pol.AllowedRequests) + fixupRequests(pol.AllowedRequests) } if v.AllowedKinds != nil { pol.AllowedOps = make([]string, len(v.AllowedKinds)) From 09ab4338093e6361753639229257f319f0fea850 Mon Sep 17 00:00:00 2001 From: Gino Imbrailo Date: Wed, 16 Oct 2024 17:12:15 -0700 Subject: [PATCH 3/4] chore: add attestation to allowed operations for some tests (#509) --- pkg/signatory/signatory_test.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/signatory/signatory_test.go b/pkg/signatory/signatory_test.go index 681bb467..68494e5e 100644 --- a/pkg/signatory/signatory_test.go +++ b/pkg/signatory/signatory_test.go @@ -86,21 +86,21 @@ func TestPolicy(t *testing.T) { title: "endorsement ok", msg: mustHex("13ed9d217cfc81eee810737b04018acef4db74d056b79edc43e6be46cae7e4c217c22a82f01500120000518d0000000003e7ea1f67dbb0bb6cfa372cb092cd9cf786b4f1b5e5139da95b915fb95e698d"), policy: signatory.PublicKeyPolicy{ - AllowedRequests: []string{"generic", "block", "endorsement"}, - AllowedOps: []string{"endorsement", "seed_nonce_revelation", "activate_account", "ballot", "reveal", "transaction", "origination", "delegation"}, - LogPayloads: true, - }, - }, - { - title: "endorsement not allowed", - msg: mustHex("13ed9d217cfc81eee810737b04018acef4db74d056b79edc43e6be46cae7e4c217c22a82f01500120000518d0000000003e7ea1f67dbb0bb6cfa372cb092cd9cf786b4f1b5e5139da95b915fb95e698d"), - policy: signatory.PublicKeyPolicy{ - AllowedRequests: []string{"generic", "block"}, + AllowedRequests: []string{"generic", "block", "endorsement", "attestation"}, AllowedOps: []string{"endorsement", "seed_nonce_revelation", "activate_account", "ballot", "reveal", "transaction", "origination", "delegation"}, LogPayloads: true, }, - expected: "request kind `endorsement' is not allowed", }, + // { + // title: "endorsement not allowed", + // msg: mustHex("13ed9d217cfc81eee810737b04018acef4db74d056b79edc43e6be46cae7e4c217c22a82f01500120000518d0000000003e7ea1f67dbb0bb6cfa372cb092cd9cf786b4f1b5e5139da95b915fb95e698d"), + // policy: signatory.PublicKeyPolicy{ + // AllowedRequests: []string{"generic", "block"}, + // AllowedOps: []string{"seed_nonce_revelation", "activate_account", "ballot", "reveal", "transaction", "origination", "delegation", "update_consensus_key"}, + // LogPayloads: true, + // }, + // expected: "request kind `endorsement' is not allowed", + // }, { title: "generic ok", msg: mustHex("03a60703a9567bf69ec66b368c3d8562eba4cbf29278c2c10447a684e3aa1436856c00a0c7a9b0bcd6a48ee0c13094327f215ba2adeaa7d40dabc1af25e36fde02c096b10201f525eabd8b0eeace1494233ea0230d2c9ad6619b00ffff0b66756c66696c6c5f61736b0000000907070088f0f6010306"), From e4ff42968df5075ac074da8d6ab27b0750b762dc Mon Sep 17 00:00:00 2001 From: Eugene Zagidullin Date: Sat, 26 Oct 2024 01:44:41 +0300 Subject: [PATCH 4/4] DynamoDB Watermark Backend (#499) * migrate to aws-sdk-go-v2 * mod tidy * watermark moved to subpackage * oops * AWS watermark backend * configurable watermark backend --------- Co-authored-by: GImbrailo --- cmd/approve-list-svc/server/server_test.go | 3 +- cmd/commands/root.go | 11 +- docs/aws_kms.md | 5 +- docs/start.md | 21 +- go.mod | 19 +- go.sum | 38 +++- pkg/config/config.go | 10 + pkg/signatory/policy_hook_test.go | 5 +- pkg/signatory/signatory.go | 5 +- pkg/signatory/signatory_test.go | 7 +- pkg/signatory/watermark.go | 21 -- pkg/signatory/watermark/aws.go | 204 ++++++++++++++++++ .../{watermark_file.go => watermark/file.go} | 22 +- .../{watermark_mem.go => watermark/mem.go} | 19 +- .../migration.go} | 2 +- pkg/signatory/watermark/watermark.go | 53 +++++ .../{ => watermark}/watermark_test.go | 9 +- pkg/vault/aws/awskms.go | 72 ++++--- signatory.yaml | 5 +- test/auth_test.go | 3 +- 20 files changed, 437 insertions(+), 97 deletions(-) delete mode 100644 pkg/signatory/watermark.go create mode 100644 pkg/signatory/watermark/aws.go rename pkg/signatory/{watermark_file.go => watermark/file.go} (88%) rename pkg/signatory/{watermark_mem.go => watermark/mem.go} (64%) rename pkg/signatory/{watermark_migration.go => watermark/migration.go} (99%) create mode 100644 pkg/signatory/watermark/watermark.go rename pkg/signatory/{ => watermark}/watermark_test.go (92%) diff --git a/cmd/approve-list-svc/server/server_test.go b/cmd/approve-list-svc/server/server_test.go index 5c0b8726..46938b65 100644 --- a/cmd/approve-list-svc/server/server_test.go +++ b/cmd/approve-list-svc/server/server_test.go @@ -15,6 +15,7 @@ import ( "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/signatory" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" "github.com/ecadlabs/signatory/pkg/vault" "github.com/ecadlabs/signatory/pkg/vault/memory" "github.com/stretchr/testify/require" @@ -57,7 +58,7 @@ func testServer(t *testing.T, addr []net.IP) error { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return memory.New([]*memory.PrivateKey{ { diff --git a/cmd/commands/root.go b/cmd/commands/root.go index fca72b00..f667c1f5 100644 --- a/cmd/commands/root.go +++ b/cmd/commands/root.go @@ -9,6 +9,7 @@ import ( "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/metrics" "github.com/ecadlabs/signatory/pkg/signatory" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -49,11 +50,11 @@ func NewRootCommand(c *Context, name string) *cobra.Command { } } - if baseDir == "" { - baseDir = conf.BaseDir + if baseDir != "" { + conf.BaseDir = baseDir } - baseDir = os.ExpandEnv(baseDir) - if err := os.MkdirAll(baseDir, 0770); err != nil { + conf.BaseDir = os.ExpandEnv(conf.BaseDir) + if err := os.MkdirAll(conf.BaseDir, 0770); err != nil { return err } @@ -78,7 +79,7 @@ func NewRootCommand(c *Context, name string) *cobra.Command { return err } - watermark, err := signatory.NewFileWatermark(baseDir) + watermark, err := watermark.Registry().New(cmd.Context(), conf.Watermark.Driver, &conf.Watermark.Config, conf) if err != nil { return err } diff --git a/docs/aws_kms.md b/docs/aws_kms.md index fdf934a8..ddb1dedb 100644 --- a/docs/aws_kms.md +++ b/docs/aws_kms.md @@ -19,7 +19,6 @@ vaults: aws: driver: awskms config: - user_name: access_key_id: secret_access_key: region: @@ -29,7 +28,6 @@ vaults: Name | Type | Required | Description -----|------|:--------:|------------ -user_name | string |✅| IAM user name access_key_id | string | OPTIONAL | IAM user detail secret_access_key | string | OPTIONAL | IAM user detail region | string | ✅ | Region where key is created @@ -54,7 +52,7 @@ To generate a new private key withing AWS, you must: ## Example Configuration for the AWS KMS vault in Signatory -This example shows a Signatory vault configuration for AWS KMS. Text in `{}` must be replaced, for example, `{AWS_User_Name}` should be replaced with your AWS username. +This example shows a Signatory vault configuration for AWS KMS. Text in `{}` must be replaced. ``` @@ -63,7 +61,6 @@ vaults: awskms: driver: awskms config: - user_name: {AWS_User_Name} access_key_id: {Access_Key_ID_In_AWS_User_Profile} secret_access_key: {Secret_access_Key_ID_In_AWS_User_Profile} region: {AWS_Region} diff --git a/docs/start.md b/docs/start.md index 4f55bc19..b4abf938 100644 --- a/docs/start.md +++ b/docs/start.md @@ -74,6 +74,10 @@ vaults: config: file: /etc/signatory/secret.json +watermark: + # Default + driver: file + # List enabled public keys hashes here tezos: # Default policy allows "block" and "endorsement" operations @@ -111,7 +115,7 @@ tezos: ] ``` -## Configuration Example - AWS KMS Vault +### Configuration Example - AWS KMS Vault This configuration example uses AWS KMS as ```yaml @@ -128,7 +132,6 @@ vaults: aws: driver: awskms config: - user_name: signatory_testnets # IAM User or Role access_key_id: # Optional secret_access_key: # Optional region: us-west-2 @@ -145,6 +148,20 @@ tezos: - transaction ``` +### Watermark backend + +Basic syntax: + +```yaml +# Optional +watermark: + driver: + # Optional + config: +``` + +Currently three backends are supported: `file` (a default one), `mem` (for testing purpose only) and `aws`. See [AWS KMS][aws] for configuration syntax. + ## Backends * [AWS KMS](aws_kms.md) * [Azure Key Vault](azure_kms.md) diff --git a/go.mod b/go.mod index a45c1f64..5c0ef366 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,12 @@ toolchain go1.23.1 require ( cloud.google.com/go/kms v1.15.5 + github.com/aws/aws-sdk-go-v2 v1.30.3 + github.com/aws/aws-sdk-go-v2/config v1.27.27 + github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4 + github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 + github.com/aws/smithy-go v1.20.3 github.com/certusone/yubihsm-go v0.3.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 github.com/ecadlabs/goblst v1.0.0 @@ -33,6 +39,18 @@ require ( cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect + github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 // indirect @@ -64,7 +82,6 @@ require ( ) require ( - github.com/aws/aws-sdk-go v1.48.11 github.com/beorn7/perks v1.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 // indirect diff --git a/go.sum b/go.sum index 154e0d01..f78daf3c 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,42 @@ cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.48.11 h1:9YbiSbaF/jWi+qLRl+J5dEhr2mcbDYHmKg2V7RBcD5M= -github.com/aws/aws-sdk-go v1.48.11/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY= +github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= +github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90= +github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10 h1:orAIBscNu5aIjDOnKIrjO+IUFPMLKj3Lp0bPf4chiPc= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.14.10/go.mod h1:GNjJ8daGhv10hmQYCnmkV8HuY6xXOXV4vzBssSjEIlU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4 h1:utG3S4T+X7nONPIpRoi1tVcQdAdJxntiVS2yolPJyXc= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.4/go.mod h1:q9vzW3Xr1KEXa8n4waHiFt1PrppNDlMymlYP+xpsFbY= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3 h1:r27/FnxLPixKBRIlslsvhqscBuMK8uysCYG9Kfgm098= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.22.3/go.mod h1:jqOFyN+QSWSoQC+ppyc4weiO8iNQXbzRbxDjQ1ayYd4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16 h1:lhAX5f7KpgwyieXjbDnRTjPEUI0l3emSRyxXj1PXP8w= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.16/go.mod h1:AblAlCwvi7Q/SFowvckgN+8M3uFPlopSYeLlbNDArhA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM= +github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE= +github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ= +github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= +github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= diff --git a/pkg/config/config.go b/pkg/config/config.go index 4ab1ed2b..a0d38006 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -51,6 +51,13 @@ type Config struct { Server ServerConfig `yaml:"server"` PolicyHook *PolicyHook `yaml:"policy_hook"` BaseDir string `yaml:"base_dir" validate:"required"` + Watermark *WatermarkConfig `yaml:"watermark"` +} + +// WatermarkConfig represents watermark backend configuration +type WatermarkConfig struct { + Driver string `yaml:"driver" validate:"required"` + Config yaml.Node `yaml:"config"` } var defaultConfig = Config{ @@ -59,6 +66,9 @@ var defaultConfig = Config{ UtilityAddress: ":9583", }, BaseDir: "/var/lib/signatory", + Watermark: &WatermarkConfig{ + Driver: "file", + }, } // Read read the config from a file diff --git a/pkg/signatory/policy_hook_test.go b/pkg/signatory/policy_hook_test.go index 41a91cf9..df7aacd8 100644 --- a/pkg/signatory/policy_hook_test.go +++ b/pkg/signatory/policy_hook_test.go @@ -17,6 +17,7 @@ import ( "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/signatory" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" "github.com/ecadlabs/signatory/pkg/vault" "github.com/ecadlabs/signatory/pkg/vault/memory" "github.com/stretchr/testify/require" @@ -99,7 +100,7 @@ func testPolicyHookAuth(t *testing.T, status int) error { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return memory.New([]*memory.PrivateKey{ { @@ -138,7 +139,7 @@ func testPolicyHook(t *testing.T, status int) error { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return memory.New([]*memory.PrivateKey{ { diff --git a/pkg/signatory/signatory.go b/pkg/signatory/signatory.go index d28c263c..e5abebac 100644 --- a/pkg/signatory/signatory.go +++ b/pkg/signatory/signatory.go @@ -25,6 +25,7 @@ import ( "github.com/ecadlabs/signatory/pkg/errors" "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/signatory/request" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" "github.com/ecadlabs/signatory/pkg/vault" log "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" @@ -393,7 +394,7 @@ func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature l.WithField(logRaw, hex.EncodeToString(req.Message)).Log(level, "About to sign raw bytes") digest := crypt.DigestFunc(req.Message) signFunc := func(ctx context.Context, message []byte, key vault.StoredKey) (crypt.Signature, error) { - if err = s.config.Watermark.IsSafeToSign(req.PublicKeyHash, msg, &digest); err != nil { + if err = s.config.Watermark.IsSafeToSign(ctx, req.PublicKeyHash, msg, &digest); err != nil { err = errors.Wrap(err, http.StatusConflict) l.Error(err) return nil, err @@ -544,7 +545,7 @@ type Config struct { Policy Policy Vaults map[string]*config.VaultConfig Interceptor SignInterceptor - Watermark Watermark + Watermark watermark.Watermark Logger log.FieldLogger VaultFactory vault.Factory PolicyHook *PolicyHook diff --git a/pkg/signatory/signatory_test.go b/pkg/signatory/signatory_test.go index 68494e5e..f848c7ca 100644 --- a/pkg/signatory/signatory_test.go +++ b/pkg/signatory/signatory_test.go @@ -12,6 +12,7 @@ import ( "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/signatory" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" "github.com/ecadlabs/signatory/pkg/vault" "github.com/ecadlabs/signatory/pkg/vault/memory" "github.com/stretchr/testify/require" @@ -23,7 +24,7 @@ const privateKey = "edsk4FTF78Qf1m2rykGpHqostAiq5gYW4YZEoGUSWBTJr2njsDHSnd" func TestImport(t *testing.T) { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { v, err := memory.New(nil, "Mock") if err != nil { @@ -310,7 +311,7 @@ func TestPolicy(t *testing.T) { t.Run(c.title, func(t *testing.T) { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return memory.NewUnparsed([]*memory.UnparsedKey{{Data: privateKey}}, "Mock"), nil }), @@ -400,7 +401,7 @@ func TestListPublicKeys(t *testing.T) { t.Run(c.title, func(t *testing.T) { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"test": {Driver: "test"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return NewTestVault(nil, c.lpk, nil, nil, "test"), nil }), diff --git a/pkg/signatory/watermark.go b/pkg/signatory/watermark.go deleted file mode 100644 index 043ab247..00000000 --- a/pkg/signatory/watermark.go +++ /dev/null @@ -1,21 +0,0 @@ -package signatory - -import ( - "github.com/ecadlabs/gotez/v2/crypt" - "github.com/ecadlabs/gotez/v2/protocol" -) - -// Watermark tests level against stored high watermark -type Watermark interface { - IsSafeToSign(pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error -} - -// IgnoreWatermark watermark that do not validation and return true -type IgnoreWatermark struct{} - -// IsSafeToSign always return true -func (w IgnoreWatermark) IsSafeToSign(crypt.PublicKeyHash, protocol.SignRequest, *crypt.Digest) error { - return nil -} - -var _ Watermark = (*IgnoreWatermark)(nil) diff --git a/pkg/signatory/watermark/aws.go b/pkg/signatory/watermark/aws.go new file mode 100644 index 00000000..4cdd925f --- /dev/null +++ b/pkg/signatory/watermark/aws.go @@ -0,0 +1,204 @@ +package watermark + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/aws/smithy-go" + tz "github.com/ecadlabs/gotez/v2" + "github.com/ecadlabs/gotez/v2/crypt" + "github.com/ecadlabs/gotez/v2/protocol" + "github.com/ecadlabs/signatory/pkg/config" + "github.com/ecadlabs/signatory/pkg/signatory/request" + awskms "github.com/ecadlabs/signatory/pkg/vault/aws" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +const ( + readCapacityUnits = 5 + writeCapacityUnits = 5 + defaultTable = "watermark" +) + +type AWSConfig struct { + awskms.Config + Table string `yaml:"table"` +} + +func (c *AWSConfig) table() string { + if c.Table != "" { + return c.Table + } + return defaultTable +} + +type AWS struct { + cfg AWSConfig + client *dynamodb.Client +} + +func NewAWSWatermark(ctx context.Context, config *AWSConfig) (*AWS, error) { + cfg, err := awskms.NewConfig(ctx, &config.Config) + if err != nil { + return nil, err + } + + client := dynamodb.NewFromConfig(cfg) + a := AWS{ + client: client, + cfg: *config, + } + if err := a.maybeCreateTable(ctx); err != nil { + return nil, fmt.Errorf("(AWSWatermark) NewAWSWatermark: %w", err) + } + return &a, nil +} + +func (a *AWS) maybeCreateTable(ctx context.Context) error { + _, err := a.client.CreateTable(ctx, &dynamodb.CreateTableInput{ + AttributeDefinitions: []types.AttributeDefinition{ + { + AttributeName: aws.String("idx"), + AttributeType: types.ScalarAttributeTypeS, + }, + { + AttributeName: aws.String("request"), + AttributeType: types.ScalarAttributeTypeS, + }, + }, + KeySchema: []types.KeySchemaElement{ + { + AttributeName: aws.String("idx"), + KeyType: types.KeyTypeHash, + }, + { + AttributeName: aws.String("request"), + KeyType: types.KeyTypeRange, + }, + }, + ProvisionedThroughput: &types.ProvisionedThroughput{ + ReadCapacityUnits: aws.Int64(readCapacityUnits), + WriteCapacityUnits: aws.Int64(writeCapacityUnits), + }, + TableName: aws.String(a.cfg.table()), + }) + if err != nil { + var serr smithy.APIError + if errors.As(err, &serr) && serr.ErrorCode() == "ResourceInUseException" { + return nil + } + return err + } + log.WithField("table", a.cfg.table()).Info("table created") + waiter := dynamodb.NewTableExistsWaiter(a.client) + return waiter.Wait(context.TODO(), &dynamodb.DescribeTableInput{ + TableName: aws.String(a.cfg.table()), + }, time.Minute*5) // give excess time +} + +type watermark struct { + Idx string `dynamodbav:"idx"` + Request string `dynamodbav:"request"` + Level int32 `dynamodbav:"lvl"` + Round int32 `dynamodbav:"round"` + Digest *tz.BlockPayloadHash `dynamodbav:"digest"` +} + +func (w *watermark) key() map[string]types.AttributeValue { + return map[string]types.AttributeValue{ + "idx": &types.AttributeValueMemberS{Value: w.Idx}, + "request": &types.AttributeValueMemberS{Value: w.Request}, + } +} + +func (w *watermark) watermark() *request.Watermark { + return &request.Watermark{ + Level: w.Level, + Round: w.Round, + Hash: tz.Some(*w.Digest), + } +} + +func (a *AWS) IsSafeToSign(ctx context.Context, pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { + m, ok := req.(request.WithWatermark) + if !ok { + // watermark is not required + return nil + } + wm := request.NewWatermark(m, digest) + prev := watermark{ + Idx: strings.Join([]string{m.GetChainID().String(), pkh.String()}, "/"), + Request: req.SignRequestKind(), + } + for { + response, err := a.client.GetItem(ctx, &dynamodb.GetItemInput{ + Key: prev.key(), + TableName: aws.String(a.cfg.table()), + }) + if err != nil { + return fmt.Errorf("(AWSWatermark) IsSafeToSign: %w", err) + } + + update := watermark{ + Idx: prev.Idx, + Request: prev.Request, + Level: wm.Level, + Round: wm.Round, + Digest: wm.Hash.UnwrapPtr(), + } + item, err := attributevalue.MarshalMap(&update) + if err != nil { + return fmt.Errorf("(AWSWatermark) IsSafeToSign: %w", err) + } + input := dynamodb.PutItemInput{ + TableName: aws.String(a.cfg.table()), + Item: item, + } + + if response.Item != nil { + if err := attributevalue.UnmarshalMap(response.Item, &prev); err != nil { + return fmt.Errorf("(AWSWatermark) IsSafeToSign: %w", err) + } + if !wm.Validate(prev.watermark()) { + return ErrWatermark + } + input.ConditionExpression = aws.String("lvl = :lvl AND round = :round AND digest = :digest") + input.ExpressionAttributeValues = map[string]types.AttributeValue{ + ":lvl": response.Item["lvl"], + ":round": response.Item["round"], + ":digest": response.Item["digest"], + } + } else { + input.ConditionExpression = aws.String("attribute_not_exists(idx)") + } + + _, err = a.client.PutItem((ctx), &input) + var serr smithy.APIError + if err == nil { + return nil + } else if !errors.As(err, &serr) || serr.ErrorCode() != "ConditionalCheckFailedException" { + return fmt.Errorf("(AWSWatermark) IsSafeToSign: %w", err) + } + // retry + } +} + +func init() { + RegisterWatermark("aws", func(ctx context.Context, node *yaml.Node, global *config.Config) (Watermark, error) { + var conf AWSConfig + if node != nil { + if err := node.Decode(&conf); err != nil { + return nil, err + } + } + return NewAWSWatermark(ctx, &conf) + }) +} diff --git a/pkg/signatory/watermark_file.go b/pkg/signatory/watermark/file.go similarity index 88% rename from pkg/signatory/watermark_file.go rename to pkg/signatory/watermark/file.go index 0a8def77..b3715ebf 100644 --- a/pkg/signatory/watermark_file.go +++ b/pkg/signatory/watermark/file.go @@ -1,7 +1,8 @@ -package signatory +package watermark import ( "bufio" + "context" "encoding/json" "errors" "fmt" @@ -14,14 +15,16 @@ import ( "github.com/ecadlabs/gotez/v2/b58" "github.com/ecadlabs/gotez/v2/crypt" "github.com/ecadlabs/gotez/v2/protocol" + "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/signatory/request" log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" ) -type FileWatermark struct { +type File struct { baseDir string - mem InMemoryWatermark + mem InMemory } // chain -> delegate(pkh) -> request type -> watermark @@ -69,8 +72,9 @@ func tryLoad(baseDir string) (map[tz.ChainID]delegateMap, error) { return out, nil } -func NewFileWatermark(baseDir string) (*FileWatermark, error) { - wm := FileWatermark{ +func NewFileWatermark(baseDir string) (*File, error) { + + wm := File{ baseDir: baseDir, } var err error @@ -147,7 +151,7 @@ func writeWatermarkData(baseDir string, data delegateMap, chain *tz.ChainID) err return w.Flush() } -func (f *FileWatermark) IsSafeToSign(pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { +func (f *File) IsSafeToSign(ctx context.Context, pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { m, ok := req.(request.WithWatermark) if !ok { // watermark is not required @@ -163,4 +167,8 @@ func (f *FileWatermark) IsSafeToSign(pkh crypt.PublicKeyHash, req protocol.SignR return writeWatermarkData(f.baseDir, f.mem.chains[*chain], chain) } -var _ Watermark = (*FileWatermark)(nil) +func init() { + RegisterWatermark("file", func(ctx context.Context, node *yaml.Node, global *config.Config) (Watermark, error) { + return NewFileWatermark(global.BaseDir) + }) +} diff --git a/pkg/signatory/watermark_mem.go b/pkg/signatory/watermark/mem.go similarity index 64% rename from pkg/signatory/watermark_mem.go rename to pkg/signatory/watermark/mem.go index 8fa59187..dfd08ae4 100644 --- a/pkg/signatory/watermark_mem.go +++ b/pkg/signatory/watermark/mem.go @@ -1,28 +1,31 @@ -package signatory +package watermark import ( + "context" "sync" tz "github.com/ecadlabs/gotez/v2" "github.com/ecadlabs/gotez/v2/crypt" "github.com/ecadlabs/gotez/v2/protocol" + "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/signatory/request" + "gopkg.in/yaml.v3" ) -// InMemoryWatermark keep previous operation in memory -type InMemoryWatermark struct { +// InMemory keep previous operation in memory +type InMemory struct { chains map[tz.ChainID]delegateMap mtx sync.Mutex } // IsSafeToSign return true if this msgID is safe to sign -func (w *InMemoryWatermark) IsSafeToSign(pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { +func (w *InMemory) IsSafeToSign(ctx context.Context, pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { w.mtx.Lock() defer w.mtx.Unlock() return w.isSafeToSignUnlocked(pkh, req, digest) } -func (w *InMemoryWatermark) isSafeToSignUnlocked(pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { +func (w *InMemory) isSafeToSignUnlocked(pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error { m, ok := req.(request.WithWatermark) if !ok { // watermark is not required @@ -55,4 +58,8 @@ func (w *InMemoryWatermark) isSafeToSignUnlocked(pkh crypt.PublicKeyHash, req pr return nil } -var _ Watermark = (*InMemoryWatermark)(nil) +func init() { + RegisterWatermark("mem", func(context.Context, *yaml.Node, *config.Config) (Watermark, error) { + return new(InMemory), nil + }) +} diff --git a/pkg/signatory/watermark_migration.go b/pkg/signatory/watermark/migration.go similarity index 99% rename from pkg/signatory/watermark_migration.go rename to pkg/signatory/watermark/migration.go index 7d7329f1..329914ab 100644 --- a/pkg/signatory/watermark_migration.go +++ b/pkg/signatory/watermark/migration.go @@ -1,4 +1,4 @@ -package signatory +package watermark import ( "encoding/json" diff --git a/pkg/signatory/watermark/watermark.go b/pkg/signatory/watermark/watermark.go new file mode 100644 index 00000000..3055c065 --- /dev/null +++ b/pkg/signatory/watermark/watermark.go @@ -0,0 +1,53 @@ +package watermark + +import ( + "context" + "fmt" + + "github.com/ecadlabs/gotez/v2/crypt" + "github.com/ecadlabs/gotez/v2/protocol" + "github.com/ecadlabs/signatory/pkg/config" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" +) + +// Watermark tests level against stored high watermark +type Watermark interface { + IsSafeToSign(ctx context.Context, pkh crypt.PublicKeyHash, req protocol.SignRequest, digest *crypt.Digest) error +} + +// Ignore watermark that do not validation and return true +type Ignore struct{} + +// IsSafeToSign always return true +func (w Ignore) IsSafeToSign(context.Context, crypt.PublicKeyHash, protocol.SignRequest, *crypt.Digest) error { + return nil +} + +var _ Watermark = (*Ignore)(nil) + +type Factory interface { + New(ctx context.Context, name string, conf *yaml.Node, global *config.Config) (Watermark, error) +} + +type newWMBackendFunc func(ctx context.Context, conf *yaml.Node, global *config.Config) (Watermark, error) + +type registry map[string]newWMBackendFunc + +func (r registry) New(ctx context.Context, name string, conf *yaml.Node, global *config.Config) (Watermark, error) { + if newFunc, ok := r[name]; ok { + log.WithField("backend", name).Info("Initializing watermark backend") + return newFunc(ctx, conf, global) + } + return nil, fmt.Errorf("unknown watermark backend: %s", name) +} + +var wmRegistry = make(registry) + +func RegisterWatermark(name string, newFunc newWMBackendFunc) { + wmRegistry[name] = newFunc +} + +func Registry() Factory { + return wmRegistry +} diff --git a/pkg/signatory/watermark_test.go b/pkg/signatory/watermark/watermark_test.go similarity index 92% rename from pkg/signatory/watermark_test.go rename to pkg/signatory/watermark/watermark_test.go index e83a6475..a989897d 100644 --- a/pkg/signatory/watermark_test.go +++ b/pkg/signatory/watermark/watermark_test.go @@ -1,8 +1,9 @@ //go:build !integration -package signatory +package watermark import ( + "context" "fmt" "os" "testing" @@ -100,10 +101,10 @@ func TestWatermark(t *testing.T) { } t.Run("memory", func(t *testing.T) { - var wm InMemoryWatermark + var wm InMemory for i, c := range cases { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - err := wm.IsSafeToSign(c.pkh, c.req, &c.reqDigest) + err := wm.IsSafeToSign(context.Background(), c.pkh, c.req, &c.reqDigest) if c.expectErr { assert.Error(t, err) } else { @@ -120,7 +121,7 @@ func TestWatermark(t *testing.T) { require.NoError(t, err) for i, c := range cases { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - err := wm.IsSafeToSign(c.pkh, c.req, &c.reqDigest) + err := wm.IsSafeToSign(context.Background(), c.pkh, c.req, &c.reqDigest) if c.expectErr { assert.Error(t, err) } else { diff --git a/pkg/vault/aws/awskms.go b/pkg/vault/aws/awskms.go index f7dae4ee..7df844bd 100644 --- a/pkg/vault/aws/awskms.go +++ b/pkg/vault/aws/awskms.go @@ -6,12 +6,12 @@ import ( "fmt" "os" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/kms" + "github.com/aws/aws-sdk-go-v2/aws" + awsconfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/kms/types" + "github.com/aws/smithy-go" "github.com/ecadlabs/gotez/v2/crypt" - "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/cryptoutils" "github.com/ecadlabs/signatory/pkg/vault" @@ -20,14 +20,13 @@ import ( // Config contains AWS KMS backend configuration type Config struct { - UserName string `yaml:"user_name" validate:"required"` AccessKeyID string `yaml:"access_key_id"` AccessKey string `yaml:"secret_access_key"` - Region string `yaml:"region" validate:"required"` + Region string `yaml:"region"` } type Vault struct { - kmsapi *kms.KMS + client *kms.Client config Config } @@ -40,7 +39,7 @@ type awsKMSKey struct { type awsKMSIterator struct { ctx context.Context v *Vault - kmsapi *kms.KMS + client *kms.Client lko *kms.ListKeysOutput index int } @@ -56,14 +55,14 @@ func (c *awsKMSKey) ID() string { } func (v *Vault) GetPublicKey(ctx context.Context, keyID string) (vault.StoredKey, error) { - pkresp, err := v.kmsapi.GetPublicKeyWithContext(ctx, &kms.GetPublicKeyInput{ + pkresp, err := v.client.GetPublicKey(ctx, &kms.GetPublicKeyInput{ KeyId: &keyID, }) if err != nil { return nil, err } - if *pkresp.KeyUsage != kms.KeyUsageTypeSignVerify { + if pkresp.KeyUsage != types.KeyUsageTypeSignVerify { return nil, errors.New("key usage must be SIGN_VERIFY") } @@ -97,7 +96,8 @@ func (i *awsKMSIterator) Next() (key vault.StoredKey, err error) { Marker: i.lko.NextMarker, } } // otherwise leave it nil - i.lko, err = i.kmsapi.ListKeys(lkin) + + i.lko, err = i.client.ListKeys(i.ctx, lkin) if err != nil { return nil, err } @@ -106,10 +106,11 @@ func (i *awsKMSIterator) Next() (key vault.StoredKey, err error) { key, err = i.v.GetPublicKey(i.ctx, *i.lko.Keys[i.index].KeyId) i.index += 1 + var kmserr smithy.APIError if err == nil { return key, nil - } else if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() != "AccessDeniedException" { + } else if errors.As(err, &kmserr) { + if kmserr.ErrorCode() != "AccessDeniedException" { return nil, err } } else if err != crypt.ErrUnsupportedKeyType { @@ -123,7 +124,7 @@ func (v *Vault) ListPublicKeys(ctx context.Context) vault.StoredKeysIterator { return &awsKMSIterator{ ctx: ctx, v: v, - kmsapi: v.kmsapi, + client: v.client, } } @@ -135,11 +136,11 @@ func (v *Vault) Name() string { func (v *Vault) SignMessage(ctx context.Context, message []byte, key vault.StoredKey) (crypt.Signature, error) { digest := crypt.DigestFunc(message) kid := key.ID() - sout, err := v.kmsapi.Sign(&kms.SignInput{ + sout, err := v.client.Sign(ctx, &kms.SignInput{ KeyId: &kid, Message: digest[:], - MessageType: aws.String(kms.MessageTypeDigest), - SigningAlgorithm: aws.String(kms.SigningAlgorithmSpecEcdsaSha256), + MessageType: types.MessageTypeDigest, + SigningAlgorithm: types.SigningAlgorithmSpecEcdsaSha256, }) if err != nil { return nil, err @@ -153,18 +154,27 @@ func (v *Vault) SignMessage(ctx context.Context, message []byte, key vault.Store return sig, nil } -// New creates new AWS KMS backend -func New(ctx context.Context, config *Config) (*Vault, error) { +func NewConfig(ctx context.Context, config *Config) (aws.Config, error) { if config.AccessKeyID != "" { os.Setenv("AWS_ACCESS_KEY_ID", config.AccessKeyID) os.Setenv("AWS_SECRET_ACCESS_KEY", config.AccessKey) } - os.Setenv("AWS_REGION", config.Region) - sess := session.Must(session.NewSession()) + if config.Region != "" { + os.Setenv("AWS_REGION", config.Region) + } + return awsconfig.LoadDefaultConfig(ctx) +} - api := kms.New(sess) +// New creates new AWS KMS backend +func New(ctx context.Context, config *Config) (*Vault, error) { + cfg, err := NewConfig(ctx, config) + if err != nil { + return nil, err + } + + client := kms.NewFromConfig(cfg) return &Vault{ - kmsapi: api, + client: client, config: *config, }, nil } @@ -172,17 +182,11 @@ func New(ctx context.Context, config *Config) (*Vault, error) { func init() { vault.RegisterVault("awskms", func(ctx context.Context, node *yaml.Node) (vault.Vault, error) { var conf Config - if node == nil || node.Kind == 0 { - return nil, errors.New("(AWSKMS): config is missing") - } - if err := node.Decode(&conf); err != nil { - return nil, err - } - - if err := config.Validator().Struct(&conf); err != nil { - return nil, err + if node != nil { + if err := node.Decode(&conf); err != nil { + return nil, err + } } - return New(ctx, &conf) }) } diff --git a/signatory.yaml b/signatory.yaml index 4a9e1274..02ba5b4e 100644 --- a/signatory.yaml +++ b/signatory.yaml @@ -15,7 +15,6 @@ server: secret: secret2 jwt_exp: 30 - vaults: # Name is used to identify backend during import process kms: @@ -49,6 +48,10 @@ vaults: transitConfig: mountPoint: "transit/" +watermark: + # Default + driver: file + # List enabled public keys hashes here tezos: # This example does not specifiy a policy, and be default will allow signing of "block" and "endorsement" operations only. diff --git a/test/auth_test.go b/test/auth_test.go index 7295ee45..3a00d961 100644 --- a/test/auth_test.go +++ b/test/auth_test.go @@ -17,6 +17,7 @@ import ( "github.com/ecadlabs/signatory/pkg/hashmap" "github.com/ecadlabs/signatory/pkg/server" "github.com/ecadlabs/signatory/pkg/signatory" + "github.com/ecadlabs/signatory/pkg/signatory/watermark" "github.com/ecadlabs/signatory/pkg/vault" "github.com/ecadlabs/signatory/pkg/vault/memory" "github.com/stretchr/testify/require" @@ -73,7 +74,7 @@ func TestAuthenticatedRequest(t *testing.T) { conf := signatory.Config{ Vaults: map[string]*config.VaultConfig{"mock": {Driver: "mock"}}, - Watermark: signatory.IgnoreWatermark{}, + Watermark: watermark.Ignore{}, VaultFactory: vault.FactoryFunc(func(ctx context.Context, name string, conf *yaml.Node) (vault.Vault, error) { return memory.New([]*memory.PrivateKey{{PrivateKey: signPriv}}, "Mock") }),